Adding and Subtracting values in Verilog - variable-assignment

I am writing a program that acts as a cash deposit box.
Everything occurs on the clock edge.
If enable is true and reset is true, the value in the box resets to 0.
If enable is true and add is 1, we add the amount to the box.
If enable is true and add is 0 we subtract from the box.
Here is my code and test bench
module cashbox(output [15:0] value, input [15:0] amount, input add,enable, clock, reset);
reg value;
always #(posedge clock)
begin
if(enable) begin
if(reset)begin
value <= 0;
end
else begin
if(add)begin
value <= value + amount;
end
else begin
value <= value - amount;
end
end
end
end
endmodule
// cashbox testbench
module cashbox_tb;
reg [15:0] amt;
reg add, en, clk, rst;
wire [15:0] value;
cashbox cb(value, amt, add, en, clk, rst);
always
#50 clk = ~clk;
initial begin
$display("Amount in box %d", value);
clk = 1; en = 1; add = 1;
rst = 1; #10 rst = 0;
amt = 100; add = 1;
#170 en = 1; #100 en = 0; // 90
#20 amt = 50; add = 0;
#180 en = 1; #100 en = 0; // 290
#20 amt = 75; add = 1;
#180 en = 1; #100 en = 0; // 490
#20 amt = 35; add = 0;
#180 en = 1; #100 en = 0; // 690
#20 amt = 50; add = 0;
#180 en = 1; #100 en = 0;
#810 $finish;
end
endmodule
Everything compiles but when I run it, it displays Amount in box X. It doesnt display a value
I changed my test bench to this
// cashbox testbench
module cashbox_tb;
reg [15:0] amt;
reg add, en, clk, rst;
wire [15:0] value;
cashbox cb(value, amt, add, en, clk, rst);
always
#50 clk = ~clk;
initial begin
$monitor("Amount in box %d",value);
clk = 0; en = 0; add = 0;
rst = 1; #10 rst = 0;
#10 amt = 100; add = 1;
#170 en = 1; #100 en = 0; // 90
#20 amt = 50; add = 0;
#180 en = 1; #100 en = 0; // 290
#20 amt = 75; add = 1;
#180 en = 1; #100 en = 0; // 490
#20 amt = 35; add = 0;
#180 en = 1; #100 en = 0; // 690
#20 amt = 50; add = 0;
#180 en = 1; #100 en = 0;
#810 $finish;
end
endmodule
And now my output is back to saying Amount in box X

$display is only executed at time 0. Change that to $monitor so that you get a display message every time value changes:
$monitor("Amount in box %d", value);
Here is the output I get with VCS:
Amount in box 0
Amount in box 1
Amount in box 0
Refer to IEEE Std 1800-2012, section "21.2.3 Continuous monitoring".
I get compile warnings with VCS, and I get a compile error with Incisive.
This can be cleaned up by changing:
module cashbox(output [15:0] value, input [15:0] amount, input add,enable, clock, reset);
reg value;
to:
module cashbox(output reg [15:0] value, input [15:0] amount, input add,enable, clock, reset);
After that change, here is my output:
Amount in box 0
Amount in box 100
Amount in box 200
Amount in box 150
Amount in box 225
Amount in box 190
Amount in box 140

Related

How to pass a reference array of variables to task?

I am trying to pass a reference array of variables. It works fine with a reference to variable, but not with an array of variables. The goal with the task is to be able to take an array of variables, and read them on every clock edge.
So this code does not work
module tb;
logic clock = 0;
logic a = 0;
logic b = 0;
task automatic read_vars(ref logic clock, input int clock_edges = 10,
const ref logic signal_array[]);
repeat (clock_edges) begin
#(posedge clock) $display("#TIME: %t Clock edge", $time);
for (int i = 0; i < signal_array.size(); i++) begin
$display("Variable %0d: %0b", i, signal_array[i]);
end
end
endtask
always begin
#1 clock = 1;
#1 clock = 0;
end
always begin
#3 b = 1;
#3 b = 0;
end
always begin
#7 a = 1;
#7 a = 0;
end
initial begin
fork
read_vars(.clock(clock), .signal_array({a, b}));
join_none
#1000;
$finish();
end
endmodule
I get the following error
read_vars(.clock(clock), .signal_array({a, b}));
|
xmvlog: *E,BADRFA (tb.sv,25|45): invalid ref argument usage because actual argument is not a variable. [SystemVerilog].
module worklib.tb:sv
But this code works.
module tb;
logic clock = 0;
logic a = 0;
logic b = 0;
task automatic read_vars(ref logic clock, ref logic a, ref logic b, input int clock_edges = 10);
repeat (clock_edges) begin
#(posedge clock) begin
$display("#TIME: %t Clock edge", $time);
$display("a = %b ", a);
$display("b = %b ", b);
end
end
endtask
always begin
#1 clock = 1;
#1 clock = 0;
end
always begin
#3 b = 1;
#3 b = 0;
end
always begin
#7 a = 1;
#7 a = 0;
end
initial begin
fork
read_vars(.clock(clock), .a(a), .b(b));
join_none
#1000;
$finish();
end
endmodule
What you are trying to do is impossible in verilog.
First of all, having a reference to an array will not help in solving the issue. Arrays contain values of something. So creation of an array with {a,b} will just copy values of the variables into the array and updating variables will not be reflected in any case.
What you are after is an array of references to variables, which is impossible in a verilog.
However, class variables are always reference. So, you can wrap your vars in a class in test bench. Here is a modified example of yours:
class V;
logic val;
endclass:V
module tb;
V a = new;
V b = new;
V arr[] = '{a, b};
logic clock = 0;
//logic a = 0;
//logic b = 0;
task automatic read_vars(ref logic clock, input int clock_edges = 10,
V signal_array[]);
repeat (clock_edges) begin
#(posedge clock) $display("#TIME: %t Clock edge", $time);
for (int i = 0; i < signal_array.size(); i++) begin
$display("Variable %0d: %0b", i, signal_array[i].val);
end
end
endtask
always begin
#1 clock = 1;
#1 clock = 0;
end
always begin
#3 b.val = 1;
#3 b.val = 0;
end
always begin
#7 a.val = 1;
#7 a.val = 0;
end
initial begin
fork
read_vars(.clock(clock), .signal_array(arr));
join_none
#1000;
$finish();
end
endmodule

VVP can't compile

I do have a problem with compiling my code :
module alu(i_a, i_b , i_oper, o_y, o_status, i_rsn, i_clk);
parameter BITS = 4;
parameter OPER = 3;
input logic signed [BITS - 1:0] i_a, i_b;
input logic [OPER-2:0] i_oper;
input logic i_clk;
input logic i_rsn;
output logic signed [BITS - 1:0] o_y;
output logic [3:0] o_status;
logic [BITS - 1:0] o_y_mux;
logic [BITS - 1:0] o_y_1;
logic [BITS - 1:0] o_y_2;
logic [BITS - 1:0] o_y_3;
logic [BITS - 1:0] o_y_4;
logic [3:0] o_status_mux;
logic [3:0] o_status_1;
logic [3:0] o_status_2;
logic [3:0] o_status_3;
logic [3:0] o_status_4;
ashift_example1 #(.BITS(BITS)) case1 (.i_a(i_a), .i_b(i_b), .o_arith_left(o_y_1), .o_error(o_status_1));
U2_to_ZM #(.BITS(BITS)) case2 (.i_a(i_a), .o_zm(o_y_2), .o_status(o_status_2));
porownanie #(.BITS(BITS)) case3 (.i_a(i_a), .i_b(i_b), .o_smaller(o_y_3), .o_error(o_status_3));
replace #(.BITS(BITS)) case4 (.i_a(i_a), .i_b(i_b), .o_replaced(o_y_4), .o_error(o_status_4));
always_comb
begin
{o_y_mux, o_status_mux} = '0;
case (i_oper)
2'b00 : o_y_mux = o_y_1;
2'b01 : o_y_mux = o_y_2;
2'b10 : o_y_mux = o_y_3;
2'b11 : o_y_mux = o_y_4;
default : o_y_mux = '0;
endcase
case (i_oper)
2'b00 : o_status_mux = o_status_1;
2'b01 : o_status_mux = o_status_2;
2'b10 : o_status_mux = o_status_3;
2'b11 : o_status_mux = o_status_4;
default : o_status_mux = '0;
endcase
end
always_ff #(posedge i_clk)
begin
if(i_rsn == 1)
begin
o_y <= o_y_mux;
o_status <= o_status_mux;
end
else
begin
o_y <= '0;
o_status <= '0;
end
end
endmodule
This is the code for the module (the module is a multiplexer).Below is testbench :
`timescale 1ns/1ps
module test_alu;
parameter BITS = 4;
parameter OPER = 3;
logic signed [BITS - 1:0] i_a, i_b;
logic [OPER-2:0] i_oper;
logic i_rsn;
logic i_clk;
logic signed [BITS - 1:0] o_y;
logic signed [BITS - 1:0] o_y_rtl;
logic [3:0] o_status;
logic [3:0] o_status_rtl;
alu #(.BITS(BITS), .OPER(OPER)) alu (.i_a(i_a), .i_b(i_b), .i_oper(i_oper), .o_y(o_y), .o_status(o_status), .i_rsn(i_rsn), .i_clk(i_clk));
//exe_unit_38_rtl alu_rtl (.i_a(i_a), .i_b(i_b), .i_oper(i_oper), .o_y(o_y_rtl), .o_status(o_status_rtl), .i_rsn(i_rsn), .i_clk(i_clk));
initial
begin
$dumpfile("alu.vcd");
$dumpvars(0,test_alu);
i_rsn = 1;
i_clk = 0;
i_oper = 2;
i_a = '0;
i_b = '0;
#1
i_clk = 1;
i_oper = 1;
i_a[3] = 1;
#1
i_clk = 0;
i_oper = 3;
i_b[3] = 0;
i_a[3] = 0;
i_a[2] = 1;
i_a[0] = 0;
#1
i_clk = 1;
i_oper = 0;
i_a[0] = 0;
i_b[1] = 1;
i_a[3] = 1;
#1
$finish;
end
endmodule
Here are also the modules that the multiplexer needs :
module ashift_example1(i_a, i_b, o_arith_left, o_error);
parameter BITS = 4;
input logic signed [BITS-1:0] i_a, i_b;
output logic signed [BITS-1:0] o_arith_left;
output logic [3:0] o_error;
integer x;
always_comb
begin
x = 0;
o_arith_left = '0;
o_error = '0;
if(i_b >= 0)
begin
o_arith_left = i_a <<< i_b;
if(i_a < 0)
begin
o_arith_left[BITS-1] = 'd1;
end
else
begin
o_arith_left[BITS-1] = 'd0;
end
end
else
begin
o_error[0] = 1;
o_arith_left = 'x;
end
for (int i = 0; i<BITS;i++)
begin
if(o_arith_left == 1)
begin
x = x+1;
end
end
if (x % 2 == 0 && o_arith_left != '0)
begin
o_error[1] = 1;
end
if(o_arith_left == '1)
begin
o_error[2] = 1;
end
end
endmodule
module U2_to_ZM (i_a , o_zm, o_status);
parameter BITS = 4;
input logic signed [BITS-1:0] i_a;
output logic signed [BITS-1:0] o_zm;
output logic [3:0] o_status;
logic signed [BITS-1:0] one;
integer x;
always_comb
begin
x = 0;
one = i_a;
o_status = '0;
if(i_a < 0)
begin
for (int i = 0; i<BITS-1; i++)
begin
one = ~i_a;
end
if(one == '1)
begin
o_status[3] = 1;
o_status[0] = 1;
o_zm = 'x;
end
else
begin
o_zm = one + 1;
end
end
else
begin
o_zm = one;
end
for (int i = 0; i<BITS;i++)
begin
if(o_zm == 1)
begin
x = x + 1;
end
end
if (x % 2 == 0 && o_zm != '0)
begin
o_status[1] = 1;
end
if (o_zm == '1)
begin
o_status[2] = 1;
end
end
endmodule
module porownanie(i_a, i_b, o_smaller, o_error);
parameter BITS = 4;
input logic signed [BITS-1:0] i_a, i_b;
output logic signed [BITS-1:0] o_smaller;
output logic [3:0] o_error;
always_comb
begin
o_error = '0;
o_smaller = '0;
if(i_a<=i_b)
begin
o_smaller = 'd1;
end
else
begin
o_smaller = '0;
end
if(o_smaller == '1)
begin
o_error[2] = 1;
end
end
endmodule
module replace(i_a, i_b, o_replaced, o_error);
parameter BITS = 4;
input logic signed [BITS-1:0] i_a, i_b;
output logic signed [BITS-1:0] o_replaced;
output logic [3:0] o_error;
integer x;
integer i;
always_comb
begin
x = 0;
i = 0;
o_replaced = '0;
o_error = '0;
if(i_b < 0 || i_b > BITS)
begin
o_error[0] = 1;
o_replaced = 'x;
end
else
begin
i = i_b;
o_replaced = i_a;
o_replaced = 1;
end
for (int i = 0; i<BITS;i++)
begin
if(o_replaced == 1)
begin
x = x+1;
end
end
if (x % 2 == 0 && o_replaced != '0)
begin
o_error[1] = 1;
end
if (o_replaced == '1)
begin
o_error[2] = 1;
end
end
endmodule
The problem is that the Multiplexer works in Windows but doesn't work in Linux I don't know why ...
Whenever I use vvp it says that it made the VCD code but the terminal doesn't respond to anything and the VCD code that I open on gthwave doesn't want to open like it is not a vcd file

SystemVerilog: $urandom_range gives values outside of range

I am getting an odd issue in ModelSim where I set an input variable to a random value in a range, but for some reason, I get a value outside of the range. All my code is included below but the essential line is:
write_addrs[i] = $urandom_range(1,NUM_ARCH_REGS);
In ModelSim, it is being assigned to 0 when it shouldn't (as shown in the waveform; the highlighted signal)...
The thing that is confounding me is that I don't ever set the write_adresses to zero except for when I set the initial signals in the INITIAL_VECTOR_VALUES block. I only modify the variable by using the $urandom_range function with the range explicitly excluding the number zero.
The main blocks of code in which I write to this variable are here:
initial begin : INITIAL_VECTOR_VALUES
advance = 0;
checkpoint = 0;
recover = 0;
write_before_checkpoint = 0;
for (int i = 0; i < NUM_READ_PORTS; i++)
read_addrs[i] = 0;
for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
write_addrs[i] = 0; //<--------------------- HERE!!!
wr_en[i] = 0;
write_data[i] = 0;
commit_en[i] = 0;
commit_data[i] = 0;
commit_addrs[i]= 0;
end
end
... and here:
task random_operations(int repeat_num);
//local vars
int operation_select, num_write, num_commit;
int current_checkpoints;
int loop_idx;
##5;
current_checkpoints = 0; //initialize
$display("Begin Random Operations # %0t", $time());
while (loop_idx < repeat_num) begin
... other stuff ...
//operand select (sets the stimulus inputs)
for (int k = 0; k < num_write; k++) begin
write_addrs[k] = $urandom_range(1,NUM_ARCH_REGS); //<--------------------- HERE!!!
$display("%0t: num_write = %0d",$time(), num_write);
$display("%0t: write_addrs[%0d] = %0d", $time(), k, write_addrs[k]);
write_data[k] = $urandom_range(1,128);
wr_en[k] = 1;
end
...
loop_idx++;
##1;
//reset signals (reset stimulus inputs)
...
end : end_while
endtask : random_operations
Does anyone know why this would be happening?
REFERENCE CODE
`timescale 1ns/1ns
module testbench;
localparam int NUM_CHECKPOINTS = 8;
localparam int NUM_ARCH_REGS = 32;
localparam int NUM_READ_PORTS = 4;
localparam int NUM_WRITE_PORTS = 2;
logic clk;
logic rst;
initial begin : CLOCK_INIT
clk = 1'b0;
forever #5 clk = ~clk;
end
default clocking tb_clk #(posedge clk); endclocking
logic [$clog2(32)-1:0] read_addrs [4];
logic [$clog2(32)-1:0] write_addrs [2];
logic wr_en [2];
logic advance; //moves tail pointer forward
logic checkpoint; //moves head pointer forward
logic recover; //moves head pointer backward (to tail)
logic write_before_checkpoint;
logic [$clog2(32)-1:0] commit_addrs [2];
logic [$clog2(128)-1:0] commit_data [2];
logic commit_en [2];
logic [$clog2(128)-1:0] write_data [2];
logic [$clog2(128)-1:0] read_data [4];
logic [$clog2(128)-1:0] write_evict_data [2];
logic enable_assertions;
cfc_rat dut(.*);
shadow_rat rat_monitor(.*);
initial begin : INITIAL_VECTOR_VALUES
advance = 0;
checkpoint = 0;
recover = 0;
write_before_checkpoint = 0;
for (int i = 0; i < NUM_READ_PORTS; i++)
read_addrs[i] = 0;
for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
write_addrs[i] = 0;
wr_en[i] = 0;
write_data[i] = 0;
commit_en[i] = 0;
commit_data[i] = 0;
commit_addrs[i]= 0;
end
end
task reset();
rst = 1;
##2;
rst = 0;
##1;
endtask : reset
task random_operations(int repeat_num);
//local vars
int operation_select, num_write, num_commit;
int current_checkpoints;
int loop_idx;
##5;
current_checkpoints = 0; //initialize
$display("Begin Random Operations # %0t", $time());
while (loop_idx < repeat_num) begin
operation_select = (loop_idx < 5) ? 1 : ((dut.dfa.chkpt_empty) ? $urandom_range(0,1) : ((dut.dfa.chkpt_full) ? 1 : $urandom_range(0,2)));
num_write = $urandom_range(0,NUM_WRITE_PORTS);
num_commit = $urandom_range(0,NUM_WRITE_PORTS);
case (operation_select)
0: begin //checkpoint
if (current_checkpoints+1 < NUM_CHECKPOINTS) begin
$display("Checkpoint # %0t", $time());
write_before_checkpoint = $urandom_range(0,1);
checkpoint = 1;
current_checkpoints++;
end
else begin
loop_idx--;
continue;
end
end
1: $display("Normal RW # %0t",$time()); //no operation, only read and write
2: begin //advance
if (current_checkpoints > 0) begin
advance = 1;
$display("Advance # %0t", $time());
current_checkpoints--;
end
else begin
loop_idx--;
continue;
end
end
3: begin //recover
$display("Recover # %0t", $time());
recover = 1;
current_checkpoints = 0;
end
default:;
endcase // operation_select
//operand select (sets the stimulus inputs)
for (int k = 0; k < NUM_READ_PORTS; k++)
read_addrs[k] = $urandom_range(0,NUM_ARCH_REGS);
for (int k = 0; k < num_write; k++) begin
write_addrs[k] = $urandom_range(1,NUM_ARCH_REGS);
$display("%0t: num_write = %0d",$time(), num_write);
$display("%0t: write_addrs[%0d] = %0d", $time(), k, write_addrs[k]);
write_data[k] = $urandom_range(1,128);
wr_en[k] = 1;
end
for (int k = 0; k < num_commit; k++) begin
commit_addrs[k] = $urandom_range(1,NUM_ARCH_REGS);
commit_data[k] = $urandom_range(1,128);
commit_en[k] = 1;
end
loop_idx++;
##1;
//reset signals (reset stimulus inputs)
checkpoint = 0;
recover = 0;
advance = 0;
write_before_checkpoint = 0;
for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
write_data[i] = 0;
wr_en[i] = 0;
end
for (int i = 0; i < NUM_READ_PORTS; i++)
read_addrs[i] = 0;
for (int i = 0; i < NUM_WRITE_PORTS; i++) begin
commit_en[i] = 0;
commit_data[i] = 0;
end
end : end_repeat
endtask : random_operations
initial begin : TEST_VECTORS
enable_assertions = 1; //for testing the monitor
reset();
random_operations(5000);
##10;
$display("Finished Successfuly! # %0t",$time());
$finish;
end
endmodule
The problem is that $urandom_range(1, NUM_ARCH_REGS) returns values from 1 to 32. But, write_addrs is declared as logic [4:0], which means it can only take on values from 0 to 31. When $urandom_range returns 32 (which is the same as 6'b10_000), the assignment in your code truncates it to 5 bits, dropping the MSB, and 5'b0_0000 is stored in write_addrs.
To fix this, only allow random values up to 31.
Change:
write_addrs[k] = $urandom_range(1, NUM_ARCH_REGS);
to:
write_addrs[k] = $urandom_range(1, NUM_ARCH_REGS-1);
Here is a complete example to demonstrate the problem:
module tb;
localparam int NUM_ARCH_REGS = 32;
logic [$clog2(32)-1:0] write_addrs [2];
initial begin
repeat (100) begin
for (int k=0; k<2; k++) begin
write_addrs[k] = $urandom_range(1, NUM_ARCH_REGS);
$write("write_addrs[%0d]=%02d ", k, write_addrs[k]);
end
$display;
end
end
endmodule
The problem you see is not specific to ModelSim; I am able to see it on 2 other simulators.

How to phase clock in system-verilog?

In System-verilog one can initialize clock and make it tick by code below:
bit clk;
initial begin
clk <= 0;
end
always #CLOCK_SEMI_PERIOD begin
clk <= ~clk;
end
But what if I want to make clock ticking with some phase? For example we have two clock, with different semiperiod and I want first one to start ticking from zero, while second one ticking from $urandom_range(6)ns.
___ ___ ___ ___
clk1 ___| |___| |___| |___| |___
____ ____ ____
clk2 _____| |____| |____| |____
I can't just write something like:
module top(output bit clk1,clk2);
parameter CLOCK1_SEMI_PERIOD = 10;
parameter CLOCK2_SEMI_PERIOD = 13;
int phase;
initial begin
clk1 <= 0;
clk2 <= 0;
phase = $urandom_range(9);
end
always #(CLOCK1_SEMI_PERIOD) begin
clk1 <= ~clk1;
end
always #(CLOCK2_SEMI_PERIOD) begin
#phase;
clk2 <= ~clk2;
end
endmodule
because it will increase second clock period by phase ns.
Then how can I implement this kind of ticking?
Use an initial/forever loop
initial begin
clk1 <= 0;
clk2 <= 0;
phase = $urandom_range(9);
fork
forever #(CLOCK1_SEMI_PERIOD)
clk1 <= ~clk1;
#phase forever #(CLOCK2_SEMI_PERIOD)
clk2 <= ~clk2;
join
end
Assignment skew concept can also be applied for introducing the required phase,
module top(output bit clk1,clk2);
parameter CLOCK1_SEMI_PERIOD = 10;
parameter CLOCK2_SEMI_PERIOD = 13;
int phase;
reg temp_1_clk2;//Added
wire temp_2_clk2;//Added
initial begin
clk1 <= 0;
clk2 <= 0;
phase = $urandom_range(9);
temp_1_clk2 = 0;
end
always #(CLOCK1_SEMI_PERIOD) begin
clk1 <= ~clk1;
end
always #(CLOCK2_SEMI_PERIOD) begin
temp_1_clk2 <= ~ temp_1_clk2;
end
assign #(phase) temp_2_clk2 = temp_1_clk2;//Introduces the required phase
always #(temp_2_clk2) begin //Output the clock
clk2 = ~ temp_2_clk2;
end
endmodule
Also let me know if there is any complication with above code or if any optimisation could be done
Thank you !

How to change and manipulate clock in SystemVerilog

I'm trying to manipulate my clock by using my own clock divider module.
module clockDivider(input logic input0,
input logic input1,
input logic clock,
output logic y);
// 00 = stop, 01 = slow, 10 = medium, 11 = fast;
if( ~input1 & ~input0 ) /*stop clock*/ ;
else if( ~input1 & input0 ) /*slow*/ ;
else if( input1 & ~input0 ) /*medium*/ ;
else if( input1 & input0 ) /*fast*/ ;
endmodule
As you can see above, according to my inputs, I will manipulate my clock and then let the step motor which is located in our FPGA board. But I couldn't figure out how to do it.
And also is there any website other than doulos? I think it is not really clear and contains just a small amount of information about System Verilog.
Thanks
You can directly having a modulo N counter to divide frequency by N.
Suppose here is your all 3 types of clock.
00 - No Clock
01 - Clock/4
02 - Clock/2
03 - Clock
Here is the code for it. Please note that its a conceptual code and not verified.
module clockDivider(input logic input0,
input logic input1,
input logic clock,
input logic reset,
output logic y);
// 00 = stop, 01 = slow, 10 = medium, 11 = fast;
parameter mod = 2;
reg [mod-1:0] count, max;
assign y = ( ~input1 & ~input0 ) ? 1'b0 : count[mod-1]; /*stop clock*/
always # (posedge clock)
begin
if( ~input1 & input0 ) /*slow*/
max <= (1 << (mod-2)) - 1'b1;
else if( input1 & ~input0 ) /*medium*/
max <= (1 << (mod-1)) - 1'b1;
else if( input1 & input0 ) /*fast*/
max <= (1 << mod) - 1'b1;
end
always # (posedge clock, negedge reset)
begin
if (!reset)
count <= 0;
else if (count == max)
count <= 0;
else
count <= count + 1'b1;
end
endmodule
By slow, medium and fast, I am going to assume that the fastest you are expecting by this logic is the speed of clock itself i.e you are implementing a clock divider.
I have assumed the following:
slow = 0.25*clock
medium = 0.5*clock
fast = clock
module clockDivider(input logic reset,
input logic input0,
input logic input1,
input logic clock,
output logic y);
// 00 = stop, 01 = slow, 10 = medium, 11 = fast;
logic delayed_y;
logic delayed_delayed_y;
logic [1:0] counter;
always #(posedge clock) begin
if (reset) begin
counter <= 'h0;
end
else begin
counter <= counter+1'b1;
end
end
always #(posedge clock) begin
if (reset) begin
delayed_y <= 1'b0;
end
else begin
delayed_y <= counter[0];
end
end
always #(posedge clock) begin
if (reset) begin
delayed_delayed_y <= 1'b0;
end
else begin
delayed_delayed_y <= counter[1];
end
end
always #(*) begin
if (reset) begin
y = 1'b0;
end
else begin
/*stop clock*/
if( ~input1 & ~input0 ) begin
y = 1'b0;
end
/*slow*/
else if( ~input1 & input0 ) begin
y = delayed_delayed_y;
end
/* medium*/
else if( input1 & ~input0 ) begin
y = delayed_y;
end
/* fast */
else if( input1 & input0 ) begin
y = clock;
end
end
end
endmodule
You can find a working example here: https://www.edaplayground.com/x/5J75
Note: If you are looking to multiply the clock, you need to use the DCM on your target FPGA. There is another method as well with a 2-input XOR gate and a clock buffer but I would stick to the DCM.