summaryrefslogtreecommitdiff
path: root/part_4/mylib/spi2adc.v
diff options
context:
space:
mode:
Diffstat (limited to 'part_4/mylib/spi2adc.v')
-rw-r--r--part_4/mylib/spi2adc.v157
1 files changed, 157 insertions, 0 deletions
diff --git a/part_4/mylib/spi2adc.v b/part_4/mylib/spi2adc.v
new file mode 100644
index 0000000..41be7ad
--- /dev/null
+++ b/part_4/mylib/spi2adc.v
@@ -0,0 +1,157 @@
+//------------------------------
+// Module name: spi2adc
+// Function: SPI interface for MCP3002 ADC
+// Creator: Peter Cheung
+// Version: 3.0
+// Date: 8 Dec 2016
+//------------------------------
+
+module spi2adc (sysclk, start, channel, data_from_adc, data_valid,
+ sdata_to_adc, adc_cs, adc_sck, sdata_from_adc);
+
+ input sysclk; // 50MHz system clock of DE0
+ input start; // Pulse to start ADC, minimum wide = clock period
+ input channel; // channel 0 or 1 to be converted
+ output [9:0] data_from_adc; // 10-bit ADC result
+ output data_valid; // High indicates that converted data valid
+ output sdata_to_adc; // Serial commands send to adc chip
+ output adc_cs; // chip select - low when converting
+ output adc_sck; // SPI clock - active during conversion
+ input sdata_from_adc; // Converted serial data from ADC, MSB first
+
+//-------------Input Ports-----------------------------
+// All the input ports should be wires
+ wire sysclk, start, sdata_from_adc;
+
+//-------------Output Ports-----------------------------
+// Output port can be a storage element (reg) or a wire
+ reg [9:0] data_from_adc;
+ reg adc_cs;
+ wire sdata_to_adc, adc_sck, data_valid;
+
+//-------------Configuration parameters for ADC --------
+ parameter SGL=1'b1; // 0:diff i/p, 1:single-ended
+ parameter MSBF=1'b1; // 0:LSB first, 1:MSB first
+
+// --- Submodule: Generate internal clock at 1 MHz -----
+ reg clk_1MHz; // 1Mhz clock derived from 50MHz
+ reg [4:0] ctr; // internal counter
+ reg tick; // 1MHz clock tick lasting 20ns, i.e. one 50MHz cycle
+ parameter TIME_CONSTANT = 5'd24; // 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; // ... to start. Initialise to make simulation easier
+ tick = 1'b0;
+ end
+
+ always @ (posedge sysclk) //
+ if (ctr==0) begin
+ ctr <= TIME_CONSTANT;
+ if (clk_1MHz==1'b0)
+ tick <= 1'b1;
+ clk_1MHz <= ~clk_1MHz; // toggle the output clock for squarewave
+ end
+ else begin
+ ctr <= ctr - 1'b1;
+ tick <= 1'b0;
+ end
+// ---- end internal clock generator ----------
+
+// ---- Detect start is asserted with a small state machine
+ // .... FF set on positive edge of start
+ // .... reset when adc_cs goes high again
+ reg [1:0] sr_state;
+ parameter IDLE = 2'b00,WAIT_CSB_FALL = 2'b01, WAIT_CSB_HIGH = 2'b10;
+ reg adc_start;
+
+ initial begin
+ sr_state = IDLE;
+ adc_start = 1'b0; // set while sending data to ADC
+ end
+
+ always @ (posedge sysclk)
+ case (sr_state)
+ IDLE: if (start==1'b0) sr_state <= IDLE;
+ else begin
+ sr_state <= WAIT_CSB_FALL;
+ adc_start <= 1'b1;
+ end
+ WAIT_CSB_FALL: if (adc_cs==1'b1) sr_state <= WAIT_CSB_FALL;
+ else sr_state <= WAIT_CSB_HIGH;
+
+ WAIT_CSB_HIGH: if (adc_cs==1'b0) sr_state <= WAIT_CSB_HIGH;
+ else begin
+ sr_state <= IDLE;
+ adc_start <= 1'b0;
+ end
+ default: sr_state <= IDLE;
+ endcase
+//------- End circuit to detect start and end of conversion
+
+
+// spi controller designed as a state machine
+// .... with 16 states (idle, and S1-S15 indicated by state value
+
+ reg [4:0] state;
+ reg adc_done, adc_din, shift_ena;
+
+ initial begin
+ state = 5'b0; adc_cs = 1'b1; adc_done = 1'b0;
+ adc_din = 1'b0; shift_ena <= 1'b0;
+ end
+
+ always @(posedge sysclk)
+ if (tick==1'b1) begin
+
+ // default outputs and state transition
+ adc_cs <= 1'b0; adc_done <= 1'b0; adc_din <= 1'b0; shift_ena <= 1'b0;
+ state <= state + 1'b1;
+ case (state)
+ 5'd0: begin
+ if (adc_start==1'b0) begin
+ state <= 5'd0; // still idle
+ adc_cs <= 1'b1; // chip select not active
+ end
+ else begin
+ state <= 5'd1; // start converting
+ adc_din <= 1'b1; // start bit is 1
+ end
+ end
+ 5'd1: adc_din <= SGL; // SGL bit
+ 5'd2: adc_din <= channel; // CH bit
+ 5'd3: adc_din <= MSBF; // MSB first bit
+ 5'd4: shift_ena <= 1'b1; // start shifting data from adc
+ 5'd15: begin
+ shift_ena <= 1'b0;
+ adc_done <= 1'b1;
+ end
+ 5'd16: begin
+ adc_cs <= 1'b1; // last state - disable chip select
+ state <= 5'd0; // go back to idle state
+ end
+ default:
+ shift_ena <= 1'b1; // unspecified states are covered by default above
+ endcase
+ end // ... always
+
+ // shift register for output data
+ reg [9:0] shift_reg;
+ initial begin
+ shift_reg = 10'b0;
+ data_from_adc = 10'b0;
+ end
+
+ always @(negedge sysclk)
+ if((adc_cs==1'b0)&&(shift_ena==1'b1)&&(tick==1'b1)) // start shifting data_in
+ shift_reg <= {shift_reg[8:0],sdata_from_adc};
+
+ // Latch converted output data
+ always @(posedge sysclk)
+ if(adc_done)
+ data_from_adc = shift_reg;
+
+ // Assign outputs to drive SPI interface to DAC
+ assign adc_sck = !clk_1MHz & !adc_cs;
+ assign sdata_to_adc = adc_din;
+ assign data_valid = adc_cs;
+endmodule \ No newline at end of file