I'm trying to implement a simple multicycle processor and I ran into some problems that I don't seem to be getting through. The code is below. I'm just experimenting right now to get this flowing. When I'm done, I'll begin implementing instructions and ALU. However, I'm stuck at this point. In the code below, I'm aware that data_memory
is never used (I'll get to there if I can resolve this), some inputs and outputs are also not used for now, x1
and x2
are just variables I created to see what's really going on. What's in definitions.v
file is self-evident.
I'm using Altera Quartus 15.1 with Verilog2001. This code compiles fine excepts some warnings due to unused stuff but when I try to simulate it with a clock period of 20ns it gives an error saying that "Error (suppressible): (vsim-3601) Iteration limit 5000 reached at time 100 ns". It also says this is suppressible but I don't know how to suppress either.
I looked up for this error and I learned that this is happening because at some point the code goes into an infinite loop. I tried to solve this by creating another variable ok
. A cycle will start by setting ok
to 0
and after microoperations for that cycle are done, I set ok
to 1
. So the cycle will not change at an improper time (it's like locking the cycle). Unfortunately, this resulted with the same error.
I tried another flow, too. Instead of cycle
and next_cycle
, I created one variable for cycle. At every rising edge of clock, I checked the current state and did things accordingly, then set the cycle for next step. Example:
always @ (posedge clk) begin
case (cycle)
3'b000: begin
MAR <= PC;
cycle <= 3'b001;
ire <= 1'b1;
x2 <= 2'b00;
3'b001: begin
...
...
This also compiles fine, and can be simulated without error! However, is not functioning correctly, giving weird(or unexpected) results. I find other approach more intuitive. So I will try to make it work.
How can I resolve/implement this?
`include "definitions.v"
module controller(
input clk,
input nres,
output reg ire,
output reg dwe,
output reg dre,
output reg [1:0] x2,
output reg [`IADR_WIDTH-1:0] i_address,
output reg [`DADR_WIDTH-1:0] d_address,
output reg [`DATA_WIDTH-1:0] data_out);
reg [2:0] cycle = 3'b000;
reg [2:0] next_cycle;
reg [`IADR_WIDTH-1:0] PC = 6'b000000;
reg [`INST_WIDTH-1:0] IR = 12'b00000_0000000;
reg [`DADR_WIDTH-1:0] MAR = 6'b000000;
reg [4:0] OPC = 5'b00000;
wire [`DATA_WIDTH-1:0] data_in;
wire [`INST_WIDTH-1:0] instruction;
reg [1:0] x1;
data_memory dmem ( .clk (clk),
.dwe (dwe),
.dre (dre),
.nres (nres),
.d_address (d_address),
.d_data (data_out),
.d_q (data_in));
instruction_memory imem ( .clk (clk),
.ire (ire),
.i_address (i_address),
.i_q (instruction));
reg ok = 1;
always @ (posedge clk) begin
cycle = (ok) ? next_cycle : cycle;
end
always @ (cycle) begin
case (cycle)
3'b000: begin
ok = 0;
MAR = PC;
next_cycle = 3'b001;
ire = 1'b1;
x2 = 2'b00;
ok = 1;
end
3'b001: begin
ok = 0;
i_address = MAR;
IR = instruction;
ire = 1'b0;
next_cycle = 3'b010;
x2 = 2'b01;
ok = 1;
end
3'b010: begin
ok = 0;
OPC = IR;
next_cycle = 3'b011;
x2 = 2'b10;
ok = 1;
end
3'b011: begin
ok = 0;
if (OPC==5'b01011) x1 = 2'b11;
PC = PC + 1;
next_cycle = 3'b000;
x2 = 2'b11;
ok = 1;
end
endcase
end
endmodule
When we write always @(signal)
in verilog, a specified sensitivity list, the logic is triggered on a change in that signal. This can lead to misunderstanding of how hardware actually works. The only hardware we have that changes on an edge is a flip-flop and you need to specify the posedge or negedge keyword for that.
When always @(signal)
is synthesised you actually get a combinatorial block, which has the effect of behaving like always @(*)
. This is an automatic sensitivity list.
So from the comments we will look at this small section of code:
always @ (*) begin
case (cycle)
3'b011: begin
ok = 0;
if (OPC==5'b01011) x1 = 2'b11;
PC = PC + 1;
next_cycle = 3'b000;
x2 = 2'b11;
ok = 1;
This a combinatorial block, triggered in the simulator when anything which can effect he output changes. Most signals are assigned to static signals, or other known values with out loops.
PC = PC +1;
The above line though updates the value of PC, this new value of PC should trigger the combinatorial block to be re-evaluated, hitting the PC increment again, etc. This all happens inside the delta cycle of the simulator.
With hardware description languages (HDLs) like Verilog we have to remember that we are describing parallel statements, not serially executed lines of code.