pigtwo
Member level 4
[Verilog] Testbench Critique - First attempt at a real testbench with verification.
Hello all,
A month or so ago I posted about wanting to get better at making testbenches. Previously I just used basic logic to create the signals and respond accordingly. This was super tedious so I've been trying to learn how to do this better. I'm trying to write my code as professionally as possible and to avoid strange looking code. If anyone is inclined I would really appreciate feedback on this testbench(or module for that matter). The module gets data from an ADC(AD7091RBRMZ) and gives it to the module that instantiated it. The test bench drives the module, simulates the functionality of the ADC, and verifies the output.
Below is the testbench:
Here is the module that the testbench drives, for reference:
I've attached the datasheet for the ADC in case that is useful. The waveforms of interest are on pages 18, 19, and 21. The code for both of these is a little more complicated than you would expect given an ADC with a SPI interface. Most of that complexity comes from the fact that the ADC can operate in two different modes and you can switch between them. I may have also just made it too complicated.
I've never seen what a serious testbench looks before. The ones I've seen just drive the clock and reset lines then provide a couple static packets of data. So I have no idea how much testing of timing I should be doing or generally the structure of testbenches. So feel free to comment about things like this if they seem odd. My intention is to possibly show code like this in an interview or display of my work. (Also, I know there are a ton of spelling errors in the comments. I'm garbage at spelling so I fix these at the very end once I'm done making changes.)
Thank you and I appreciate any advice or input!
Hello all,
A month or so ago I posted about wanting to get better at making testbenches. Previously I just used basic logic to create the signals and respond accordingly. This was super tedious so I've been trying to learn how to do this better. I'm trying to write my code as professionally as possible and to avoid strange looking code. If anyone is inclined I would really appreciate feedback on this testbench(or module for that matter). The module gets data from an ADC(AD7091RBRMZ) and gives it to the module that instantiated it. The test bench drives the module, simulates the functionality of the ADC, and verifies the output.
Below is the testbench:
Code Verilog - [expand] 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Written by: // // Create Date: 02/08/2018 // Design Name: // Module Name: AD7091RBRMZ_TB // Target Devices: AD7091RBRMZ // Description: This module is meant to simulate the action of the AD7091RBRMZ. It // will verify very simple timing constraints such as tQuiet, time // between the conversion input going low to CS going low. Verification // of functionality is also tested by driving the module and verifing // the output. // // Revision: 1.0 // Revision 1.0 - Sucessful simulation. // Additional Comments: There are three main aspects of the AD7091RBRMZ which needs // modeling. First when the module is powered on a soft // reset is required. This is verified by the model. Next // there are two different modes of operation. Normal operation // and using the busy indicator. In normal operation the // conversion pin is brought low. 650ns later CS can be brought // low to start clocking out data. When using the busy indicator // conversion is brought low. CS must be brought low within 650ns // then the SDO line will be pulled low to indicate the conversion // is complete. Swaping between these modes is supported by this // model. Full details in the data sheet. // ////////////////////////////////////////////////////////////////////////////////// module AD7091RBRMZ_TB(); // Define basic signals reg clk, reset; // Define UUT signals wire ADC_conv; // To ADC wire ADC_cs; // To ADC wire ADC_sclk; // To ADC wire ready; // Tells the user(TB in this case) that another conversion can be started/data from the previous conversion is ready. wire[11:0] data_out; // Data from the module(UUT); reg ADC_sdo; // From ADC reg convert; // Input to the UUT that tells it to start a conversion. reg use_busy_sig; // Input to the UUT that tells it to use the busy signal. This takes a single conversion before it takes effect. reg [11:0] timing_errors; // Number of timing errors found. reg [11:0] verification_errors; // Number of failures in verification. reg [11:0] data_verify; // Data read from the UUT. reg [11:0] data_expected; // Data we expect from the UUT. reg [11:0] data_adc; // Data the model will output. This will increment after each conversion. reg [11:0] data_adc_temp; // Data used to send data out from the model. reg [15:0] num_conversions; // Number of conversions we will verify. reg use_busy_indicator; // Denotes whether the busy indicator should be used. // Signals used in TB reg[15:0] time1, time2; reg[15:0] counter; // UUT instance AD7091RBRMZ ADC_UUT( .clk(clk), .reset(reset), .ADC_conv(ADC_conv), .ADC_cs(ADC_cs), .ADC_sclk(ADC_sclk), .ADC_sdo(ADC_sdo), .ready(ready), .data_out(data_out), .convert(convert), .use_busy_sig(use_busy_sig) ); // clock generation initial begin forever #10 clk = ~clk; // 50 MHz clock end initial begin // ADC model - This block is meant to model the functionality of the ADC // Give initial values to registers clk = 0; counter = 0; ADC_sdo = 1'bz; convert = 0; data_verify = 0; data_adc = 2729; data_adc_temp = data_adc; data_expected = data_adc; num_conversions = 20; timing_errors = 0; verification_errors = 0; use_busy_indicator = 0; use_busy_sig = 1; time1 = $time; time2 = $time; // Start main process // Reset the module start_reset(); // Verify soft reset wait_for_soft_reset(); //$display("Soft reset complete! Device correctly initialized!"); // Wait for conversion forever while(1) begin @(negedge ADC_conv) // Conversion received start_conversion(); // Start conversion task data_adc = data_adc + 1; // Increment input data. end end initial begin // This block drives the module to get data then verifies it. @(posedge ready) // Wait for the UUT to be ready while(num_conversions != 0) begin // Get num_conversion about of conversions. #20 convert = 1; @(negedge ready) #20 convert = 0; if(num_conversions[3]) // Swap the busy signal every once in a while. use_busy_sig = ~use_busy_sig; @(posedge ready) data_verify = data_out; if(data_verify != data_expected) begin verification_errors = verification_errors + 1; end data_expected = data_expected + 1; num_conversions = num_conversions - 1; end $display("Verification complete!"); $display("Timing errors found: %d.", timing_errors); $display("Verification errors found: %d.", verification_errors); end task start_reset; // Reset task. begin @(posedge clk) reset = 0; #55 @(negedge clk) reset = 1; end endtask task wait_for_soft_reset; // This task verifies that the power on soft reset is done correctly. begin @(negedge ADC_conv) time1 = $time; @(negedge ADC_cs) time2 = $time; if(time2-time1 < 650) begin $display("Soft reset failed: CS pulled low too early. %d ns", time2-time1); timing_errors = timing_errors + 1; end ADC_sdo = $random; while(counter < 7 && !ADC_cs) begin // Count how many data bits are clocked out by the module. This should be 3-7 to initiate a soft reset. @(negedge ADC_sclk or posedge ADC_cs) // ADC_cs is in this so we can escape the while loop. ADC_sdo = $random; counter = counter + 1; end ADC_sdo = 1'bz; // ADC_sdo should be highz in between conversions. if(counter < 2) begin // Verify number of data bits clocked out. Report errors. $display("CS pulled high too early during soft reset. Only %d bit(s) were clocked out.", counter + 1); timing_errors = timing_errors + 1; end else if(counter > 6) begin $display("CS pulled high too late during soft reset. %d bits were clocked out.", counter + 1); timing_errors = timing_errors + 1; end // Another conversion must be started to finish the soft reset. No data is clocked out during this time. But user must wait the 650ns anyway. @(negedge ADC_conv) time1 = $time; time2 = $time; @(posedge ADC_conv) while(time2 - time1 < 650) begin // Wait 650ns. #1 time2 <= $time; if(!ADC_conv) begin $display("New conversion started too soon since soft reset. Only %d ns since end of last conv.", time2-time1); timing_errors = timing_errors + 1; end end use_busy_indicator = 0; // At the end of a soft reset the busy indicator is reset to zero. end endtask task soft_reset; // This is called when a module trys to soft reset the ADC. It verifys this is done correctly. begin @(negedge ADC_conv) time1 = $time; time2 = $time; @(posedge ADC_conv) while(time2 - time1 < 650) begin #1 time2 <= $time; if(!ADC_conv) begin $display("New conversion started too soon since soft reset. Only %d ns since end of last conv.", time2-time1); timing_errors = timing_errors + 1; end end use_busy_indicator = 0; end endtask task start_conversion; begin counter = 0; data_adc_temp = data_adc; // Verify tQuiet timing time2 = $time; if(time2 - time1 < 50) begin $display("tQuiet timing violated. Only %d ns since the end of last conversion. Time: %d ns.", time2-time1, time2); timing_errors = timing_errors + 1; end time1 = $time; time2 = $time; if(use_busy_indicator) begin //// The two different conversion methods split here. @(negedge ADC_cs) // Wait for CS to go low. time2 = $time; if(time2 - time1 > 650) begin // If CS goes low after 650ns this means the module wants to turn the busy indicator off. This will take effect the next conversion. $display("Reseting busy indicator."); use_busy_indicator = 0; end while(time2 - time1 < 650) begin // Wait for the conversion time to pass. #1 time2 = $time; end #8 ADC_sdo = 0; // Bring SDO low to indicate the end of the conversion. time1 = $time; time2 = $time; while(!ADC_cs) begin @(negedge ADC_sclk or posedge ADC_cs) // Clock data out on sclk. CS is there to escape the while loop. time2 = $time; counter = counter + 1; if(counter > 12) begin // Once enough data is sent drive SDO to highz. #8 ADC_sdo = 1'bz; end else begin time1 = $time; #7 ADC_sdo = data_adc_temp[11]; data_adc_temp = {data_adc_temp[10:0], data_adc_temp[11]}; end end //// Verify enough data has been sent and check for soft reset request. if(counter < 3) begin // CS pulled up to early. Only 1-2 bits clocked out. Error. $display("CS pulled low to early during the clocking out of data from conversion. Only %d bits read out. Time: %d ns.", counter, time2); timing_errors = timing_errors + 1; end else if(counter < 2 && counter < 8) begin // This initiates a soft reset. $display("Soft reset initiated. Time: %d ns.", time2); soft_reset(); end else if(counter > 7 && counter < 14) begin // Counter pulled low too early(too late for soft reset). Error. $display("CS pulled low to early during the clocking out of data from conversion. Only %d bits read out. Time: %d", counter, time2); timing_errors = timing_errors + 1; end else if(counter == 14) begin // Only correct option that isn't a reset. //$display("Data sucessfuly read from ADC! Time: %d ns.", time2); end else begin // Counter counted past 13 clock cycles. Too many. Error. $display("Too many data bits clocked out during conversion. %d bits. Time: %d.", counter-1, time2); end time1 = $time - 20; // 20ns subtracted to compensate for this measurment being taken from the rising edge of CS instead of the rising edge of sclk. time2 = $time; end else begin @(negedge ADC_cs) // There are two reasons CS can go low here. Either to start clocking data out or set the busy indicator. This depends on whether 650ns has passed yet. time2 = $time; if(time2 - time1 < 650) begin // ADC_cs brought low too early. This could mean they are setting the busy signal or it is a timing violation. @(posedge ADC_cs or posedge ADC_sclk) if(ADC_cs) begin // This means ADC_cs triggered first. This would mean they are trying to set the busy signal. @(negedge ADC_cs) use_busy_indicator = 1; time2 = $time; if(time2 - time1 < 650) begin $display("CS pulled low too early while setting busy indicator. Second edge too early. CS only held low %d ns. Time: %d ns.", time2-time1, time2); timing_errors = timing_errors + 1; end end else begin $display("CS pulled low too early when starting conversion. Data not valid and simulation may be broken past this point. CS only held low %d ns. Time: %d ns.", time2-time1, time2); timing_errors = timing_errors + 1; end end // Start clocking out data. #18 ADC_sdo = data_adc_temp[11]; data_adc_temp = {data_adc_temp[10:0], data_adc_temp[11]}; counter = 0; time1 = $time; time2 = $time; while(!ADC_cs) begin @(negedge ADC_sclk or posedge ADC_cs) // Clock data out on negative edge of ADC_sclk time2 = $time; counter = counter + 1; if(counter > 11) begin // Drive SDO highz after data. #15 ADC_sdo = 1'bz; end else begin #7 ADC_sdo = data_adc_temp[11]; data_adc_temp = {data_adc_temp[10:0], data_adc_temp[11]}; end end //// Verify enough data has been sent and check for soft reset request. if(counter < 2) begin // CS pulled up to early. Only 1-2 bits clocked out. Error. $display("CS pulled low to early during the clocking out of data from conversion. Only %d bits read out. Time: %d ns.", counter, time2); timing_errors = timing_errors + 1; end else if(counter < 1 && counter < 9) begin // This initiates a soft reset. $display("Soft reset initiated. Time: %d ns.", time2); soft_reset(); end else if(counter > 6 && counter < 12) begin // Counter pulled low too early(too late for soft reset). Error. $display("CS pulled low to early during the clocking out of data from conversion. Only %d bits read out. Time: %d", counter, time2); timing_errors = timing_errors + 1; end else if(counter == 12) begin // Only correct option that isn't a reset. //$display("Data sucessfuly read from ADC! Time: %d ns.", time2); end else begin // Counter counted past 13 clock cycles. Too many. Error. $display("Too many data bits clocked out during conversion. %d bits. Time: %d.", counter, time2); end time1 = $time - 10; // 10ns subtracted to compensate for this measurment being taken from the rising edge of CS instead of the falling edge of sclk. time2 = $time; end end endtask endmodule
Here is the module that the testbench drives, for reference:
Code Verilog - [expand] 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 15:02:33 01/06/2018 // Design Name: // Module Name: AD7091RBRMZ // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module AD7091RBRMZ( input wire clk, input wire reset, // ADC signals output reg ADC_conv, output reg ADC_cs, output wire ADC_sclk, input wire ADC_sdo, // Control signals output reg ready, output reg[11:0] data_out, input wire convert, input wire use_busy_sig ); // Local parameters //localparam use_busy_sig = 1; // This tells the module to use the busy signal feature or not. localparam[4:0] start = 0, idle = 1, convert_start = 2, convert_start_busy = 3, convert_cs_busy = 4, convert_cs = 5, convert_wait_busy = 6, convert_get_data = 7, convert_wait = 8, convert_get_data_extra = 9, convert_done = 10, convert_get_data_delay = 11, reset_convert_start = 12, reset_convert_wait1 = 13, reset_convert_get_data = 14, reset_quiet_wait = 15, reset_convert_wait2 = 16, convert_wait_ce_busy = 17, convert_hold_ce = 18, convert_delay = 19, convert_stop_busy_sig = 20; // Define local registers and wires reg ADC_conv_nxt, ADC_cs_nxt, ready_nxt, ADC_ce, ADC_ce_nxt; reg busy_init, busy_init_nxt; // This register says whether the busy signal has been initialized. reg busy_sig, busy_sig_nxt; // This register saves the value of 'use_busy_sig' when a conversion starts. reg[4:0] state, state_nxt; reg[5:0] counter, counter_nxt; reg[11:0] data_out_nxt; assign ADC_sclk = (ADC_ce) ? clk : 0; // ADC clock gating. /* always@(posedge clk or negedge clk) begin ADC_sclk <= (ADC_ce_nxt) ? clk : busy_init; end */ always@(negedge clk) begin // ADC_ce needs to transistion on negative edges to avoid glitches. if(!reset) begin ADC_ce <= 0; end else begin ADC_ce <= ADC_ce_nxt; end end // Define sequential logic always@(posedge clk) begin if(!reset) begin ADC_conv <= 1; ADC_cs <= 1; ready <= 0; counter <= 0; data_out <= 0; busy_init <= 0; busy_sig <= 0; state <= start; end else begin ADC_conv <= ADC_conv_nxt; ADC_cs <= ADC_cs_nxt; ready <= ready_nxt; counter <= counter_nxt; data_out <= data_out_nxt; busy_init <= busy_init_nxt; busy_sig <= busy_sig_nxt; state <= state_nxt; end end // Define combinational logic always @* begin // Define default transistions ADC_conv_nxt = ADC_conv; ADC_cs_nxt = ADC_cs; ADC_ce_nxt = ADC_ce; ready_nxt = ready; counter_nxt = counter; data_out_nxt = data_out; busy_init_nxt = busy_init; busy_sig_nxt = busy_sig; state_nxt = state; case(state) start: begin ADC_conv_nxt = 1; ADC_cs_nxt = 1; ADC_ce_nxt = 0; ready_nxt = 0; counter_nxt = 0; data_out_nxt = 0; busy_init_nxt = 0; busy_sig_nxt = 0; state_nxt = reset_convert_start; end reset_convert_start: begin // This is the start of the reset process. Must be done before device can be used. ADC_conv_nxt = 0; state_nxt = reset_convert_wait1; end reset_convert_wait1: begin // Start conversion and wait for conversion to finish(650ns). ADC_conv_nxt = 1; counter_nxt = counter + 1; if(counter == 33) begin counter_nxt = 0; ADC_ce_nxt = 1; ADC_cs_nxt = 0; state_nxt = reset_convert_get_data; end end reset_convert_get_data: begin // Start reading data, but pull CS high prematurely(triggers soft reset). counter_nxt = counter + 1; if(counter == 4) begin // We need to halt the read between the second and eighth bit. ADC_cs_nxt = 1; ADC_ce_nxt = 0; counter_nxt = 0; state_nxt = reset_quiet_wait; end end reset_quiet_wait: begin // Wait a few cycles for the quiet wait time. Then start a new conversion. counter_nxt = counter + 1; if(counter == 4) begin ADC_conv_nxt = 0; state_nxt = reset_convert_wait2; end end reset_convert_wait2: begin // Wait for conversion to finish then reset is done. ADC_conv_nxt = 1; counter_nxt = counter + 1; if(counter == 34) begin counter_nxt = 0; state_nxt = idle; end end idle: begin ADC_conv_nxt = 1; ADC_cs_nxt = 1; ADC_ce_nxt = 0; ready_nxt = 1; counter_nxt = 0; data_out_nxt = data_out; if(convert) begin // A conversion request is received ready_nxt = 0; busy_sig_nxt = use_busy_sig; if(busy_init) // Determine if the busy signal is initialized or not. state_nxt = convert_start_busy; // States with the prefix 'busy' are the path taken when the busy indicator is used. else state_nxt = convert_start; end else begin state_nxt = idle; end end convert_start_busy: begin ADC_conv_nxt = 0; state_nxt = convert_cs_busy; end convert_cs_busy: begin ADC_conv_nxt = 1; if(!busy_sig) begin ADC_cs_nxt = 1; counter_nxt = 0; state_nxt = convert_stop_busy_sig; end else begin ADC_cs_nxt = 0; state_nxt = convert_wait_busy; end end convert_stop_busy_sig: begin // To reset the busy signal you must bring CS low after the conversion time. This state waits 650ns, then brings CS low. counter_nxt = counter + 1; ADC_cs_nxt = 1; if(counter == 33) begin counter_nxt = 0; ADC_cs_nxt = 0; state_nxt = convert_wait_busy; end end convert_wait_busy: begin // Wait for the busy interupt if(!ADC_sdo) begin // The ADC finished the conversion. counter_nxt = 0; state_nxt = convert_wait_ce_busy; end end convert_wait_ce_busy: begin // This state is nessesary because CE is triggered off the negative edge. The time from CS going low to SDO going low is 18ns. This means the negative edge has been missed. One extra delay is needed. ADC_ce_nxt = 1; state_nxt = convert_get_data; end convert_start: begin // This begins the conversion process without using the busy signal. ADC_conv_nxt = 0; ADC_cs_nxt = 1; state_nxt = convert_cs; end convert_cs: begin // Brings conv high and keeps CS high unless the busy signal is trying to be initialized. ADC_conv_nxt = 1; ADC_cs_nxt = 1; if(busy_sig) // CS needs to be brought low once during the conversion time to set the busy signal. ADC_cs_nxt = 0; state_nxt = convert_wait; end convert_wait: begin // Wait for conversion to finish. counter_nxt = counter + 1; ADC_cs_nxt = 1; if(counter == 33) begin counter_nxt = 0; ADC_cs_nxt = 0; state_nxt = convert_get_data_delay; end end convert_get_data_delay: begin // An extra ADC_ce_nxt = 1; data_out_nxt = {data_out[10:0], ADC_sdo}; state_nxt = convert_get_data; end convert_get_data: begin ADC_ce_nxt = 1; data_out_nxt = {data_out[10:0], ADC_sdo}; counter_nxt = counter + 1; if(counter == 10) begin // We have all 12 bits(11 iterations plus the one bit sampled as we entered this state). if(busy_init) begin // We have to hold CE high for an extra cycle if using busy signal. if(!busy_sig) // If the busy signal is low and the busy signal is initialized then we want to reset it. Only the flag is set here. The work was done earlier. busy_init_nxt = 0; state_nxt = convert_get_data_extra; end else begin if(busy_sig) busy_init_nxt = 1; // If the busy signal is not initialized but the busy_sig is high then we need to initialize it. Only the flag is set here. The work was done earlier. state_nxt = convert_done; end end end convert_get_data_extra: begin // This state gets another bit from the ADC. This is nessesary because when using the busy signal you have to clock the interupt bit out. data_out_nxt = {data_out[10:0], ADC_sdo}; ADC_ce_nxt = 1; state_nxt = convert_hold_ce; // We need a delay so another conversion doesn't start too soon. end convert_hold_ce: begin // This state allows the clock to clock one more time to put SDO to high z. ADC_ce_nxt = 0; ADC_cs_nxt = 0; ready_nxt = 0; state_nxt = convert_delay; // We need a delay so another conversion doesn't start too soon. This helps me tQuiet. This one is specificly when using the busy signal. end convert_delay: begin ready_nxt = 0; state_nxt = convert_done; end convert_done: begin // This is a delay so another conversion doesn't start too soon. ADC_cs_nxt = 1; ADC_ce_nxt = 0; ready_nxt = 1; state_nxt = idle; end default: state_nxt = start; endcase end endmodule
I've attached the datasheet for the ADC in case that is useful. The waveforms of interest are on pages 18, 19, and 21. The code for both of these is a little more complicated than you would expect given an ADC with a SPI interface. Most of that complexity comes from the fact that the ADC can operate in two different modes and you can switch between them. I may have also just made it too complicated.
I've never seen what a serious testbench looks before. The ones I've seen just drive the clock and reset lines then provide a couple static packets of data. So I have no idea how much testing of timing I should be doing or generally the structure of testbenches. So feel free to comment about things like this if they seem odd. My intention is to possibly show code like this in an interview or display of my work. (Also, I know there are a ton of spelling errors in the comments. I'm garbage at spelling so I fix these at the very end once I'm done making changes.)
Thank you and I appreciate any advice or input!