summaryrefslogtreecommitdiff
path: root/part_3/ex10a/spi2dac.v
blob: 586a23148d9e2149217b2d6013ea377b2778f725 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//------------------------------
// Module name: spi2dac
// Function: SPI interface for MPC4911 DAC
// Creator:  Peter Cheung
// Version:  2.0
// Date:     8 Nov 2016
//------------------------------

module spi2dac (sysclk, data_in, load, dac_sdi, dac_cs, dac_sck, dac_ld);

	input	sysclk;			// 50MHz system clock of DE1
	input	[9:0]	data_in;	// input data to DAC
	input	load;				// Pulse to load data to dac
	output dac_sdi;		// SPI serial data out
	output dac_cs;			// chip select - low when sending data to dac
	output dac_sck;		// SPI clock, 16 cycles at half sysclk freq
	output dac_ld;

//-------------Input Ports-----------------------------
// All the input ports should be wires
	wire			sysclk, load;
	wire [9:0]	data_in;
	
//-------------Output Ports-----------------------------
// Output port can be a storage element (reg) or a wire
	reg			dac_cs, dac_ld;
	wire			dac_sck, dac_sdi;
	
	parameter	BUF=1'b1;		// 0:no buffer, 1:Vref buffered
	parameter	GA_N=1'b1;		// 0:gain = 2x, 1:gain = 1x
	parameter	SHDN_N=1'b1;	// 0:power down, 1:dac active
	
	wire [3:0] cmd = {1'b0,BUF,GA_N,SHDN_N};  // wire to VDD or GND
	
	// --- internal 1MHz symmetical clock generator -----
	reg			clk_1MHz;	// 1Mhz clock derived from 50MHz
	reg [4:0]	ctr;			// internal counter
	
	parameter	TC = 5'd24;  // Terminal count - change this for diff clk freq
	initial begin
		clk_1MHz = 0;			// don't need to reset - don't care if it is 1 or 0 to start
		ctr = 5'b0;				//  ... Initialise when FPGA is configured
	end
		
	always @ (posedge sysclk)    
	  if (ctr==0) begin
		  ctr <= TC;
		  clk_1MHz <= ~clk_1MHz; // toggle the output clock for squarewave
		end
	  else
		  ctr <= ctr - 1'b1;
	// ---- end internal 1MHz symmetical clock generator ----------

	// ---- FSM to detect rising edge of load and falling edge of dac_cs
	// .... sr_state set on posedge of load
	// .... sr_state reset when dac_cs goes high at the end of DAC output cycle
	reg [1:0] 	sr_state;
	parameter	IDLE  = 2'b00,WAIT_CSB_FALL = 2'b01, WAIT_CSB_HIGH = 2'b10;
	reg			dac_start;		 // set if a DAC write is detected
	
	initial begin
		sr_state = IDLE;
		dac_start = 1'b0;	// set while sending data to DAC
		end
	
	always @ (posedge sysclk)  // state transition
		case (sr_state)
			IDLE:	if (load==1'b1) sr_state <= WAIT_CSB_FALL;
			WAIT_CSB_FALL: if (dac_cs==1'b0) sr_state <= WAIT_CSB_HIGH;
			WAIT_CSB_HIGH: if (dac_cs==1'b1) sr_state <= IDLE;
			default: sr_state <= IDLE;
		endcase
	
	always @ (*)
		case (sr_state)
			IDLE: dac_start = 1'b0;
			WAIT_CSB_FALL: dac_start = 1'b1;
			WAIT_CSB_HIGH: dac_start = 1'b0;
			default: dac_start = 1'b0;
		endcase
		
	//------- End circuit to detect start and end of conversion	state machine

	//------- spi controller FSM
	// .... with 17 states (idle, and S1-S16 
	// .... for the 16 cycles each sending 1-bit to dac)
	reg [4:0] 	state;
	
	initial	begin	
		state = 5'b0; dac_ld = 1'b0; dac_cs = 1'b1; 
		end
		
	always @(posedge clk_1MHz)  // FSM state transition
		case (state)
			5'd0:	if (dac_start == 1'b1)    // waiting to start
						state <= state + 1'b1; 
					else 
						state <= 5'b0; 
			5'd17: 	state <= 5'd0;  // go back to idle state
			default: state <= state + 1'b1;	// default go to next state
		endcase
	
	always @ (*)	begin			// FSM output
		dac_cs = 1'b0;  dac_ld = 1'b1;
		case (state)
			5'd0: 	dac_cs = 1'b1;
			5'd17: 	begin dac_cs = 1'b1; dac_ld = 1'b0; end
			default: begin dac_cs = 1'b0;	dac_ld = 1'b1;	end
			endcase
		end //always
	// --------- END of spi controller FSM
	
	// shift register for output data
	reg [15:0] shift_reg;
	initial	begin	
		shift_reg = 16'b0; 
		end

	always @(posedge clk_1MHz)
		if((dac_start==1'b1)&&(dac_cs==1'b1))		// parallel load data to shift reg
			shift_reg <= {cmd,data_in,2'b00};
		else 													// .. else start shifting
			shift_reg <= {shift_reg[14:0],1'b0};
	
	// Assign outputs to drive SPI interface to DAC
			assign dac_sck = !clk_1MHz&!dac_cs;
			assign dac_sdi = shift_reg[15];
endmodule