I'm looking for some help understanding a synthesis error I run into frequently with Verilog code on an FPGA platform (Lattice ECP5U).
Here's a simplified setup that produces the error. riscv
is a main module that instantiates a submodule called icache
(some module ports omitted for simplicity).
module riscv();
reg [31:0] ir; // instruction register
icache icache(
[...ports omitted...]
.instruction (ir)
);
endmodule
module icache(
[...ports omitted...]
output wire [31:0] instruction
);
reg [31:0] ram [0:NUM_CACHE_LINES];
always @(posedge clk) begin
[...logic omitted...]
instruction <= ram[0]; // *** error here
end
endmodule
The error I get, at the starred line above, is:
Assignment target 'instruction' must be of type reg, genvar, or logic.
I understand that instruction
is marked as a wire
in the port list, and the synthesis tool is telling me that it is expecting a flip flop. The problem is that I want the instruction fetched from icache
to be registered in the top module, in the flip flops called ir
, not registered twice as it would be if I were to mark instruction
as output reg [31:0] instruction
in icache
's port list.
Basically, I don't understand why the synthesis tool can't "see" that instruction
is just passing its value through to the ir
flip flops in the parent module. Is this not legal Verilog?
And, anyway, given that the tool doesn't see things my way, what's the usual solution here? Should I just register instruction
in the icache
module and then have the ir
variable in the top riscv
module be marked as a wire
type instead? Or should I register the value in both modules and just trust (+ verify) that the synthesis tool only generates one set of 32 flip flops for the instruction fetched from the icache?
1 Answer 1
Should I just register
instruction
in theicache
module and then have their
variable in the topriscv
module be marked as awire
type instead?
Yes, that is the usual solution.
module riscv();
wire [31:0] ir; // instruction register
icache icache(
[...ports omitted...]
.instruction (ir)
);
endmodule
module icache(
[...ports omitted...]
output reg [31:0] instruction
);
reg [31:0] ram [0:NUM_CACHE_LINES];
always @(posedge clk) begin
[...logic omitted...]
instruction <= ram[0];
end
endmodule
In icache
, you make a procedural assignment (an assignment inside an always
block) to instruction
. That means you can not declare instruction
as a wire
in that module. It must be declared as a reg
(or logic
if you enable SystemVerilog features in your tools).
Keep in mind that the following code is synthesized into flip flops in the module where it resides:
always @(posedge clk) begin
instruction <= ram[0];
-
\$\begingroup\$ Secondly, declaring
reg
only does not mean that a flip flop will be created i.e.reg
only does not mean memory element. \$\endgroup\$Im Groot– Im Groot2024年02月20日 06:06:41 +00:00Commented Feb 20, 2024 at 6:06 -
\$\begingroup\$ @ImGroot Yes, I realize that, but do you think there's a possibility that the synthesis tool would interpret a value that is reg'd in two places as an instruction to create two banks of flip flops for the value, and then require an extra clock cycle for a FF-to-FF transfer of the value? I'm new to Verilog, so maybe I'm misunderstanding how the tool would interpret that. \$\endgroup\$jemalloc– jemalloc2024年02月20日 14:43:27 +00:00Commented Feb 20, 2024 at 14:43
-
2\$\begingroup\$ @jemalloc: No, the
reg
keyword does not work that way; simulation and synthesis depend on how you assign to thereg
signal. \$\endgroup\$toolic– toolic2024年02月20日 14:46:07 +00:00Commented Feb 20, 2024 at 14:46 -
\$\begingroup\$ I think of it this way: every port of an instantiated block is a wire when viewed from the calling module. You are dropping a physical circuit in at that point. \$\endgroup\$Troutdog– Troutdog2024年03月02日 15:22:33 +00:00Commented Mar 2, 2024 at 15:22