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