/// Verilog solution to bean counter DQ/MC exam question. // Includes structural and behavioral solution (beancount) and test // code (beantest). // Resolution of clock simulation. `define clock_period 7 module beancount(count, pd1, pd2, clk, reset); input pd1; // Photodetector 1. Blocked when 1. input pd2; // Photodetector 2. Blocked when 1. input clk; // Clock. input reset; // Reset. output count; // The number of beans. reg [15:0] count; // Number of beans observed. wire pd1, pd2, clk, reset; // Time needed for a bean to move from pd1 to pd2, in cycles. reg [15:0] gap_time; // Counter measuring pd1 to pd2 time. reg [15:0] gap_timer; // Number of cycles before middle of bean reached. reg [15:0] bean_timer; reg last_pd1, last_pd2; // Previous state of photodetector. // Update last state of pd1 and pd2 /// Updated: last_pd1, last_pd2 always @( posedge clk ) begin last_pd1 <= pd1; last_pd2 <= pd2; end // Increment bean time counter or reset when new bean cluster arrives. /// Updated: gap_timer always @( posedge clk ) if( !last_pd1 && pd1 ) gap_timer <= 0; // New clump, reset. else gap_timer <= gap_timer + 1; /// Updated: count, bean_timer, gap_time always @( posedge clk ) if( reset ) begin // Reset. count <= 0; bean_timer <= 16'hffff; gap_time <= 16'hffff; end else if( pd2 && !last_pd2 ) begin // At pd2 clump arrives. gap_time <= gap_timer; bean_timer <= gap_timer >> 1; end else if( pd2 && last_pd2 && bean_timer === 0 ) begin // At pd2, middle of bean. bean_timer <= gap_time; count <= count + 1; end else if( pd2 && last_pd2 && bean_timer !== 0 ) begin // At pd2, other clump location. bean_timer <= bean_timer - 1; end endmodule // beancount // Alternate behavioral implementation. // // Not synthesizable because some registers are modified // in multiple behaviors (initial and always constructs). // module beancount_x(count, pd1, pd2, clk, reset); input pd1; // Photodetector 1. Blocked when 1. input pd2; // Photodetector 2. Blocked when 1. input clk; // Clock. input reset; // Reset. output count; // The number of beans. reg [15:0] count; // Number of beans observed. wire pd1, pd2, clk, reset; // Time needed for a bean to move from pd1 to pd2, in cycles. reg [15:0] gap_time; // Counter measuring pd1 to pd2 time. reg [15:0] gap_timer; // Number of cycles before middle of bean reached. reg [15:0] bean_timer; always @( posedge reset ) begin count = 0; bean_timer = 16'hffff; gap_time = 16'hffff; gap_timer = 16'hffff; end always @( posedge pd1 ) begin gap_timer = 0; end always @( posedge pd2 ) begin gap_time = gap_timer; bean_timer = gap_timer >> 1; end always @( posedge clk ) begin if( pd1 ) gap_timer = gap_timer + 1; if( pd2 ) begin if( bean_timer == 0 ) begin bean_timer = gap_time; count = count + 1; end bean_timer = bean_timer - 1; end end endmodule // beancount_x // Behavioral description of master-slave edge triggered register. module DREG16(q,d,c); output q; input d,c; reg [15:0] q, nextq; wire [15:0] d; wire c; always @( posedge c ) nextq <= d; always @( negedge c ) q <= nextq; endmodule // DREG16 // Behavioral description of master-slave edge triggered flip-flop. module DREG1(q,d,c); output q; input d,c; reg q, nextq; wire d; wire c; always @( posedge c ) nextq <= d; always @( negedge c ) q <= nextq; endmodule // DREG1 module mux_16_4(out,in0,in1,in2,in3,c); input in0, in1, in2, in3, c; output out; reg [15:0] out; wire [15:0] in0, in1, in2, in3; wire [1:0] c; always @( in0 or in1 or in2 or in3 or c ) case( c ) 0: out = in0; 1: out = in1; 2: out = in2; 3: out = in3; endcase endmodule module mux_16_2(out,in0,in1,c); input in0, in1, c; output out; reg [15:0] out; wire [15:0] in0, in1; wire c; always @( in0 or in1 or c ) case( c ) 0: out = in0; 1: out = in1; endcase endmodule // Bean Counter Implicit Structural Description // module beancount_s(count, pd1, pd2, clk, reset); input pd1; // Photodetector 1. Blocked when 1. input pd2; // Photodetector 2. Blocked when 1. input clk; // Clock. input reset; // Reset. output count; // The number of beans observed. wire [15:0] count; // Number of beans observed. wire pd1, pd2, clk, reset; wire last_pd1, last_pd2; DREG1 last_pd1_reg(last_pd1,pd1,clk); DREG1 last_pd2_reg(last_pd2,pd2,clk); wire new_clump_pd1 = pd1 && ~last_pd1; wire new_clump_pd2 = pd2 && ~last_pd2; wire [15:0] gap_timer; wire [15:0] next_gap_timer; mux_16_2 ngt_mux(next_gap_timer, gap_timer+16'd1, 16'd0, new_clump_pd1); DREG16 gap_timer_counter(gap_timer,next_gap_timer,clk); wire [15:0] gap_time; DREG16 gap_time_reg(gap_time,gap_timer,new_clump_pd2); wire [15:0] bean_time; wire [15:0] next_bean_time; mux_16_4 nbt_mux(next_bean_time, bean_time - 16'd1, gap_time, {1'b0,gap_timer[15:1]}, {1'b0,gap_timer[15:1]}, {new_clump_pd2, bean_time === 0}); DREG16 bean_time_reg(bean_time,next_bean_time,clk); wire [15:0] next_count = reset ? 0 : bean_time === 0 && last_pd2 ? count + 1 : count; DREG16 bean_count(count,next_count,clk); endmodule // beancount_s // Bean Counter Test Bench // module beantest(); integer beantime; // Time for bean to move from pd1 to pd2. integer beans, beans1; // Number of beans simulated by testbench. reg pd1, pd2, clk; wire [15:0] cnt; integer errors; integer last_error; // Difference between executed and actual. reg reset; // Instantiate a bean counter. beancount_s bc(cnt,pd1,pd2,clk,reset); initial begin:INIT integer inter_clump; // Time between beans. integer min_bean_time; // Fastest bean speed. integer max_bean_time; // Slowest bean speed. integer oldbeantime; // Initialize simulation variables. min_bean_time = 10 * `clock_period; // Fastest bean speed. max_bean_time = 10 * min_bean_time; // Slowest bean speed. beantime = min_bean_time; oldbeantime = 0; beans = 0; // Testbench bean count. errors = 0; last_error = 0; // Initialize simulated hardware. pd1 = 0; pd2 = 0; clk = 0; // Generate reset signal. reset = 0; #1; reset = 1; # (2 * `clock_period); reset = 0; # (2 * `clock_period); // Simulate at least 50 beans. while( beans < 50 ) begin // End of previous clump reaches pd1. pd1 <= 0; // Choose amount of time between bean clumps. inter_clump = ($random>>1) % ( 5 * max_bean_time ); # inter_clump; // Beginning of new clump reaches pd1; pd1 <= 1; // Choose bean speed, amount of time for one bean to pass // a point. (E.g., pd1.) beantime = min_bean_time + ($random>>1) % ( max_bean_time - min_bean_time ); // Prevent new clump from smashing into previous clump // between photodetectors. if( inter_clump + beantime < oldbeantime ) beantime = oldbeantime + inter_clump ; oldbeantime = beantime; begin:CLUMP forever begin // Bean arrives at pd1. # beantime; // Bean arrives at pd2. beans = beans + 1; if( $random & 'b1 ) disable CLUMP; end end end // while ( beans < 50 ) // Wait for last bean, plus time for circuit to count it. # ( 2 * beantime ); if( errors === 0 ) $display("Beans counted properly.\n"); else $display("*** ERROR: bean count off by %d.\n", beans - cnt); $stop; end // block: INIT // Clock for bean counter circuit. always #`clock_period clk = ~clk; // Make sure bean counter count agrees with testbench . always @ ( beans ) beans1 <= #(beantime * 0.75 ) beans; always @( beans1 ) if( cnt - beans1 !== last_error ) begin errors = errors + 1; last_error = cnt - beans1; if( errors <= 5 ) $display("Wrong bean count, %d should be %d.\n", cnt,beans1); if( errors == 5 ) $display("Ignoring future errors.\n"); $stop; end // Set pd2 to lag pd1 by a beantime. always @( posedge pd1 ) pd2 <= #beantime 1; always @( negedge pd1 ) pd2 <= #beantime 0; endmodule // beantest