Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migen outputs incorrect verilog in specific use-case #119

Open
rohitk-singh opened this issue Jul 4, 2018 · 6 comments
Open

Migen outputs incorrect verilog in specific use-case #119

rohitk-singh opened this issue Jul 4, 2018 · 6 comments

Comments

@rohitk-singh
Copy link
Contributor

rohitk-singh commented Jul 4, 2018

I wrote a Migen code which used MultiReg for signal synchronization across clock domains and the relevant section is similar to the test code below:

from migen import *
from migen.fhdl import verilog
from migen.genlib.cdc import MultiReg

class Test(Module):
    def __init__(self, n):
        counter = Signal(8)
        done = Signal(n)
        regs = []

        for i in range(n):
            regs.append(MultiReg(counter[i], done[i]))

        self.specials += regs
        self.sync.rx += counter.eq(counter + 1)

print(verilog.convert(Test(2)))

The verilog output of this code is:

Incorrect Code Section
wire [1:0] done;
always @(*) begin
	done <= 2'd0;
	done[0] <= multiregimpl01;
	done[1] <= multiregimpl11;
end
Complete generated verilog file (cleaned up) :
module top(
	input rx_clk,
	input rx_rst,
	input sys_clk,
	input sys_rst
);


reg [7:0] counter = 8'd0;
wire [1:0] done;
(* no_retiming = "true" *) reg multiregimpl00 = 1'd0;
(* no_retiming = "true" *) reg multiregimpl01 = 1'd0;
(* no_retiming = "true" *) reg multiregimpl10 = 1'd0;
(* no_retiming = "true" *) reg multiregimpl11 = 1'd0;

always @(*) begin
	done <= 2'd0;
	done[0] <= multiregimpl01;
	done[1] <= multiregimpl11;
end

always @(posedge rx_clk) begin
	counter <= (counter + 1'd1);
	if (rx_rst) begin
		counter <= 8'd0;
	end
end

always @(posedge sys_clk) begin
	multiregimpl00 <= counter[0];
	multiregimpl01 <= multiregimpl00;
	multiregimpl10 <= counter[1];
	multiregimpl11 <= multiregimpl10;
end

endmodule

Issue

The problem with above generated verilog is that done[1:0] is declared as wire instead of being declared as reg since it is being assigned inside the always block.

It might be fault of the coding style used in the above test code, but creating object with Test(1) instead of Test(2) gives correct output: https://hastebin.com/jiwomizawe.scala

Also, if I refactor the code to this:

from migen import *
from migen.fhdl import verilog
from migen.genlib.cdc import MultiReg

class Test(Module):
    def __init__(self, n):
        counter = Signal(8)
        done = []
        regs = []

        for i in range(n):
            done.append(Signal(name='done{}'.format(i)))
            regs.append(MultiReg(counter[i], done[i]))

        self.specials += regs
        self.sync.rx += counter.eq(counter + 1)

print(verilog.convert(Test(2)))

then I get correct verilog output -> https://hastebin.com/bujarijeqe.scala

I can definitely use the refactored code to get intended output but just wanted to confirm whether this is a bug or not.

@sbourdeauducq
Copy link
Member

Yes it's a bug; can you patch it, test it carefully, and send a PR?

@rohitk-singh
Copy link
Contributor Author

Sure, let me give it a try.

rohitk-singh added a commit to rohitk-singh/migen that referenced this issue Jul 5, 2018
…) blocks, as reg instead of wire

Fixes m-labs#119

Currently, the migen code assumes that all outputs from specials must be
wire, which is true logically. But wires can also be assigned inside
always(*) blocks, which then should be declared as regs.

Solution:
We find out all the output signals from specials (set special_outs_only).
Then we make a list which has comb statements grouped-by-target signals.
We look whether signals in this list are present in `special_outs_only`
set or not. If they are , we remove those signals from the wires set.

Signed-off-by: Rohit Singh <rohit91.2008@gmail.com>
@rohitk-singh
Copy link
Contributor Author

@sbourdeauducq, @jordens and @whitequark: Could you please review this commit: rohitk-singh@b3206e2

It seems to solve the above issue.

@sbourdeauducq
Copy link
Member

sbourdeauducq commented Jul 6, 2018

If the way to solve it is to allow special outputs to be reg, then the comb assign in MultiRegImpl and maybe other places (please check) becomes unnecessary.

@whitequark whitequark added the bug label Nov 10, 2018
@gsomlo
Copy link
Contributor

gsomlo commented Feb 20, 2019

sort-of related question: in general, it is considered "good verilog" to use only blocking assignments in combinational (e.g., "always @(*)" blocks, and I noticed that migen uses nonblocking "<=" throughout all such blocks. I wonder if simply generating "="s instead of "<="s for combinational (note: NOT synchronous) blocks wouldn't be a better fix?
EDIT: nevermind, I notice this is already being discussed in a few other migen PRs; sorry for the noise :)

@whitequark
Copy link
Contributor

Triage: fixed in nMigen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants