This post will be about programming a reaction timer in verilog and make it ready for FPGA implementation which in our case will be on the BASYS2 (Spartan 3E) board but can be programmed for any other board by simply changing the .ucf file. The reaction timer or reflex tester will check and time how fast you can respond after seeing a visual stimulus or in other words it will test your hand eye co-ordination.
The code for this is a bit more busy than any of my other projects, but it has been heavily commented so just by reading the code you can easily understand what is going on. The code will be followed by a video demonstration in Isim along with it working on the FPGA board.
Here we will be using three inputs; reset, start, stop and one output led along with displaying the time on the seven segment display.
The multiplexer module has 4 inputs which will have data passed to them from the main module. The outputs are connected to the 7 segment display. For detailed explanation on the above code visit the post: 7 Segment LED multiplexing in Verilog.
And now for the code of the reaction / reflex tester:
If you go through the above code you will note that its pretty self explanatory as all steps have been commented. The only thing worth mentioning here is the
I tried to write the code in a very easy way and commented it in detail so its easy to read and understand. Below is the video demonstration of this code working.
And here is the simulation of the above code:
The code for this is a bit more busy than any of my other projects, but it has been heavily commented so just by reading the code you can easily understand what is going on. The code will be followed by a video demonstration in Isim along with it working on the FPGA board.
Here we will be using three inputs; reset, start, stop and one output led along with displaying the time on the seven segment display.
- When the reset button is pressed it will obviously reset all registers and counters and will make the system ready for the next reflex test. Also when in this state it will display a "Hi" message on the seven segment display to welcome the user and to show that it is ready for the next input.
- When the start button is pressed the output led will turn on after a random interval between 0 seconds and 10 seconds. The random time is achieved using the random number generator which I have explained in detail in my previous post. Randomness here is very important, if the test did not have a random delay we could mentally memorize the delay between reset and the stimulus thus ruining the test.
- After the random delay is achieved the led will turn on along with the timer.
- The user is now to press the stop button. This will stop the timer and display your reaction time. The normal reaction time should be between 0.15s and 0.30s.
module muxer( input clock, input reset, input [3:0] fourth, input [3:0] third, input [3:0] second, input [3:0] first, output a_m, output b_m, output c_m, output d_m, output e_m, output f_m, output g_m, output dp_m, output [3:0] an_m ); //The Circuit for 7 Segment Multiplexing - localparam N = 18; reg [N-1:0]count; //the 18 bit counter which allows us to multiplex at 1000Hz always @ (posedge clock or posedge reset) begin if (reset) count <= 0; else count <= count + 1; end reg [3:0]sseg; //the 4 bit register to hold the data that is to be output reg [3:0]an_temp; //register for the 4 bit enable reg reg_dp; always @ (*) begin case(count[N-1:N-2]) //MSB and MSB-1 for multiplexing 2'b00 : begin sseg = first; an_temp = 4'b1110; reg_dp = 1'b1; end 2'b01: begin sseg = second; an_temp = 4'b1101; reg_dp = 1'b0; end 2'b10: begin sseg = third; an_temp = 4'b1011; reg_dp = 1'b0; end 2'b11: begin sseg = fourth; an_temp = 4'b0111; reg_dp = 1'b1; end endcase end assign an_m = an_temp; reg [6:0] sseg_temp; always @ (*) begin case(sseg) 4'd0 : sseg_temp = 7'b1000000; //display 0 4'd1 : sseg_temp = 7'b1111001; //display 1 4'd2 : sseg_temp = 7'b0100100; //display 2 4'd3 : sseg_temp = 7'b0110000; //display 3 4'd4 : sseg_temp = 7'b0011001; //display 4 4'd5 : sseg_temp = 7'b0010010; //display 5 4'd6 : sseg_temp = 7'b0000010; //display 6 4'd7 : sseg_temp = 7'b1111000; //display 7 4'd8 : sseg_temp = 7'b0000000; //display 8 4'd9 : sseg_temp = 7'b0010000; //display 9 4'd10 : sseg_temp = 7'b0001001; //to display H 4'd11 : sseg_temp = 7'b1001111; //to display I default : sseg_temp = 7'b0111111; //dash endcase end assign {g_m, f_m, e_m, d_m, c_m, b_m, a_m} = sseg_temp; assign dp_m = reg_dp; endmodule
The multiplexer module has 4 inputs which will have data passed to them from the main module. The outputs are connected to the 7 segment display. For detailed explanation on the above code visit the post: 7 Segment LED multiplexing in Verilog.
And now for the code of the reaction / reflex tester:
module reaction_main( input clock, reset, start, stop, output led, output [3:0] an, output a, b, c, d, e, f, g, dp ); reg [3:0] regd3, regd2, regd1, regd0; //the main output registers wire db_start, db_stop; reg dffstr1, dffstr2, dffstp1, dffstp2; always @ (posedge clock) dffstr1 <= start; always @ (posedge clock) dffstr2 <= dffstr1; assign db_start = ~dffstr1 & dffstr2; //monostable multivibrator to detect only one pulse of the button always @ (posedge clock) dffstp1 <= stop; always @ (posedge clock) dffstp2 <= dffstp1; assign db_stop = ~dffstp1 & dffstp2; //monostable multivibrator to detect only one pulse of the button // Instantiate the 7 segment multiplexing module muxer display ( .clock(clock), .reset(reset), .fourth(regd3), .third(regd2), .second(regd1), .first(regd0), .a_m(a), .b_m(b), .c_m(c), .d_m(d), .e_m(e), .f_m(f), .g_m(g), .dp_m(dp), .an_m(an) ); //Block for LFSR random number generator reg [28:0] random, random_next, random_done; //**29 bit register to keep track upto 10 seconds. reg [4:0] count_r, count_next_r; //to keep track of the shifts. 5 bit register to count up to 30 wire feedback = random[28] ^ random[26]; always @ (posedge clock or posedge reset) begin if (reset) begin random <= 29'hF; //An LFSR cannot have an all 0 state, thus reset to FF. count_r <= 0; end else begin random <= random_next; count_r <= count_next_r; end end always @ (*) begin random_next = random; //default state stays the same count_next_r = count_r; random_next = {random[27:0], feedback}; //shift left the xor'd every posedge clock if (count_r == 29) begin count_next_r = 0; random_done = random; //assign the random number to output after 30 shifts end else begin count_next_r = count_r + 1; random_done = random; //keep previous value of random end end //random number block ends reg [3:0] reg_d0, reg_d1, reg_d2, reg_d3; //registers that will hold the individual counts (* KEEP = "TRUE" *)reg [1:0] sel, sel_next; //for KEEP attribute see note below localparam [1:0] idle = 2'b00, starting = 2'b01, time_it = 2'b10, done = 2'b11; reg [1:0] state_reg, state_next; reg [28:0] count_reg, count_next; always @ (posedge clock or posedge reset) begin if(reset) begin state_reg <= idle; count_reg <= 0; sel <=0; end else begin state_reg <= state_next; count_reg <= count_next; sel <= sel_next; end end reg go_start; always @ (*) begin state_next = state_reg; //default state stays the same count_next = count_reg; sel_next = sel; case(state_reg) idle: begin //DISPLAY HI HERE sel_next = 2'b00; if(db_start) begin count_next = random_done; //get the random number from LFSR module state_next = starting; //go to next state end end starting: begin if(count_next == 500000000) // **500M equals a delay of 10 seconds. and starting from 'rand' ensures a random delay begin state_next = time_it; //go to next state end else begin count_next = count_reg + 1; end end time_it: begin sel_next = 2'b01; //start the timer state_next = done; end done: begin if(db_stop) begin sel_next = 2'b10; //stop the timer end end endcase case(sel_next) //this case statement that will control what is sent to the 7 segment based on the sel signal 2'b00: //hi begin go_start = 0; //make sure timer module is off regd0 = 4'd12; regd1 = 4'd11; regd2 = 4'd10; regd3 = 4'd12; end 2'b01: //timer begin go_start = 1'b1; //enable start signal to start timer regd0 = reg_d0; regd1 = reg_d1; regd2 = reg_d2; regd3 = reg_d3; end 2'b10: //stop timer begin go_start = 1'b0; regd0 = reg_d0; regd1 = reg_d1; regd2 = reg_d2; regd3 = reg_d3; end 2'b11: //Although this condition is of no use to us it is placed here for the sake of completion, case statements left uncompleted will create a latch in implementation begin regd0 = 4'd12; //4'd12 to siplay '-' regd1 = 4'd12; regd2 = 4'd12; regd3 = 4'd12; go_start = 1'b0; end default: begin regd0 = 4'd12; regd1 = 4'd12; regd2 = 4'd12; regd3 = 4'd12; go_start = 1'b0; end endcase end //the stopwatch block reg [18:0] ticker; //19 bits needed to count up to 500K bits wire click; //the mod 500K clock to generate a tick ever 0.01 second always @ (posedge clock or posedge reset) begin if(reset) ticker <= 0; else if(ticker == 500000) //if it reaches the desired max value of 500K reset it ticker <= 0; else if(go_start) //only start if the input is set high ticker <= ticker + 1; end assign click = ((ticker == 500000)?1'b1:1'b0); //click to be assigned high every 0.01 second always @ (posedge clock or posedge reset) begin if (reset) begin reg_d0 <= 0; reg_d1 <= 0; reg_d2 <= 0; reg_d3 <= 0; end else if (click) //increment at every click begin if(reg_d0 == 9) //xxx9 - the 0.001 second digit begin //if_1 reg_d0 <= 0; if (reg_d1 == 9) //xx99 begin // if_2 reg_d1 <= 0; if (reg_d2 == 5) //x599 - the two digit seconds digits begin //if_3 reg_d2 <= 0; if(reg_d3 == 9) //9599 - The minute digit reg_d3 <= 0; else reg_d3 <= reg_d3 + 1; end else //else_3 reg_d2 <= reg_d2 + 1; end else //else_2 reg_d1 <= reg_d1 + 1; end else //else_1 reg_d0 <= reg_d0 + 1; end end //If count_reg == 500M - check if 'stop' key is pressed, if yes disable led, otherwise enable it. If count_reg ~= 500M keep led off. assign led = ((count_reg == 500000000)?((db_stop == 1)?1'b0:1'b1):1'b0); endmodule
If you go through the above code you will note that its pretty self explanatory as all steps have been commented. The only thing worth mentioning here is the
(* KEEP = "TRUE" *)attribute. This attribute has nothing to do with the working of the code, it is needed for implementation only. If you are going to simulate this code you can remove this attribute as it wont have any effect on its working. The reason I felt to add it was because during implementation and synthesis Xilinx for some reason decided to remove the sel signal entirely after giving this warning:
I checked it up with Xilinx documentation and it was a known problem with the version of Xilinx I am using. For some reason Xilinx thought it would optimize the design by removing one of my most vital signals :) So in order to force Xilinx to keep the signal the KEEP attribute must be used.Xst:1710 - FF/Latch <sel_3> (without init value) has a constant value of 0 in block <reaction_main>. This FF/Latch will be trimmed during the optimization process.
I tried to write the code in a very easy way and commented it in detail so its easy to read and understand. Below is the video demonstration of this code working.
And here is the simulation of the above code:
I have a quick question, I tried programming the first code to my Basys2 board and I'm getting a strange outcome from the initial start up. The AN1(M13) is showing a 'C' character with the top two parts bright, and the bottom parts of the 'C' character dim and no clock timer or rest active. I've tried diagnosing this problem with now luck, I would just like some incite on what you believe it is. Thank you.
ReplyDelete