Question

I'm having trouble understanding Kansas Lava's behaviour when an RTL block contains multiple assignments to the same register. Here's version number 1:

foo :: (Clock c) => Signal clk Bool
foo = runRTL $ do
    r <- newReg True
    r := low    
    return $ var r

This behaves as I expected it:

*Main> takeS 10 foo :: Seq Bool
low | low | low | low | low | low | low | low | low | low | ? .

The generated VHDL is:

architecture str of assignments is
  signal sig_2_o0 : std_logic;
begin
  sig_2_o0 <= '0';
  OUTPUT <= sig_2_o0;
end architecture str;

However, I hoped this other version would also work:

foo = runRTL $ do
    r <- newReg True

    r := low
    r := high
    return $ var r

But it doesn't, and the second assignment isn't taken into account:

*Main> takeS 10 foo :: Seq Bool
low | low | low | low | low | low | low | low | low | low | ? .

The reason I'm confused is because reg and var are defined in terms of a full clock cycle, so it's not like I could do impossible-to-synthesize things like branch based on r and then reassign a new value to it. So why doesn't this second form work?

It's not just a simulation issue either: the generated VHDL for the second version clearly shows that the second assignment is thrown away at generation time:

architecture str of assignments2 is
  signal sig_2_o0 : std_logic;
begin
  sig_2_o0 <= '0';
  OUTPUT <= sig_2_o0;
end architecture str;

So basically, I would have expected the output to be more like

architecture str of assignments2 is
  signal sig_2_o0 : std_logic;
begin
  sig_2_o0 <= '0';
  sig_2_o0 <= '1';
  OUTPUT <= sig_2_o0;
end architecture str;

but I'm not sure what that would/should mean in VHDL.

Was it helpful?

Solution

The problem is that you are using multiple non-blocking statements to assign the signal.

  sig_2_o0 <= '0';
  sig_2_o0 <= '1';

This translates to:

at next event assign '0' to sig_2_o0.
at next event assign '1' to sig_2_o0.

This is different than using blocking assignments:

  sig_2_o0 := '0';
  sig_2_o0 := '1';

Which would translate to:

assign '0' to sig_2_o0.
assign '1' to sig_2_o0.

Blocking assignments

When you use blocking assignments the value is clearly defined. First it will set to '0', then override it with '1'. In this example there should be no effect from the first blocking assignment for either simulation or synthesized hardware. You can think of it as there is 0 delay between the first assignment and the second. This means you have a pulse of 0 width, which is actually nothing. It is equivalent to having just the last assignment, with the first omitted entirely. One caveat is that if you put a delay on the assignments, e.g. "after 1 ns" then you will notice the first assignment followed by the second in simulation. In hardware, delays are ignored and so there would be no change from adding delays. In fact, inserting delays in RTL that is intended to be synthesized is strongly discouraged for this reason. It is highly desirable that the hardware matches the simulation, and adding delays can introduce mismatches.

Non-blocking assignments

But when you use non-blocking assignments the simulator has two assignments scheduled for the next time event. Set the signal to '1' and at the same time, set it to '0'. So which scheduled assignment will the signal take? There is no way to know. It may be either value since it is incorrectly assigned. Every lint checker and synthesis tool on the planet should throw an error when encountering multiple non-blocking assignments like this. It may be possible to simulate it, but there is clearly a problem with the RTL.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top