// 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