// Verilog solution to bean counter DQ/MC exam question. // Includes structural and behavioral solution (beancount) and test // code (beantest). `timescale 1ns/1ns // Resolution of clock simulation. `define clock_period 7 //`define BEHAVIORAL `ifdef BEHAVIORAL 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 // Updates: 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. // Updates: gap_timer. always @( posedge clk ) if( last_pd1 === 1'b0 && pd1 === 1'b1 ) gap_timer <= 0; // New clump, reset. else gap_timer <= gap_timer + 1; // Updates: 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 === 1'b1 && last_pd2 === 1'b0 ) begin // At pd2 clump arrives. gap_time <= gap_timer; bean_timer <= {1'b0,gap_timer[15:1]}; end else if( pd2 === 1'b1 && last_pd2 === 1'b1 && bean_timer === 0 ) begin // At pd2, middle of bean. bean_timer <= gap_time; count <= count + 1; end else if( pd2 === 1'b1 && last_pd2 === 1'b1 && bean_timer !== 0 ) begin // At pd2, other clump location. bean_timer <= bean_timer - 1; end endmodule // beancount `else // !ifdef BEHAVIORAL // 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 // Bean Counter Implicit Structural Description 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 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 = new_clump_pd1 ? 0 : gap_timer + 1; 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; wire [15:0] bean_time_m1 = bean_time - 1; assign next_bean_time = new_clump_pd2 ? {1'b0,gap_timer[15:1]} : bean_time === 0 ? gap_time : bean_time_m1; 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 `endif // !ifdef BEHAVIORAL `define TEST `ifdef TEST 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 bean counter. beancount 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; 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; // Amount of time between bean clumps. inter_clump = ($random>>1) % ( 5 * max_bean_time ); # inter_clump; // Beginning of new clump reaches pd1; pd1 <= 1; // 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 // 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 // initial begin // 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"); end // Set pd2 to lag pd1 by a beantime. always @( posedge pd1 ) pd2 <= #beantime 1; always @( negedge pd1 ) pd2 <= #beantime 0; endmodule // beantest `endif // ifdef TEST