0
\$\begingroup\$

I am rewriting a VGA controller in (I hope) a better way; I have a 'blank' signal which means that the current pixel is outside of the visible area, and I have the gut feeling it would be better to branch that signal outside of the "pixel processing" process (race conditions?).

I am trying to translate something like this:

-- Horizontal procedure:
h_proc : process(pix_clk) 
begin
    if(rising_edge(pix_clk)) then
        -- counts the pixel clocks
        h_cnt <= h_cnt + 1; 
        -- updates the blank signal (whether in visible area or not)
        if(h_cnt <= HRES and v_cnt <= VRES) then
            blank <= false;
        -- generates synchronisation signals
        else
            blank <= true;
            if(h_cnt > HRES+HFP and h_cnt < HRES+HFP+HSP) then
                h_sync <= '0';
            else
                h_sync <= '1';
            end if;
        end if;
    end if;
end process;

Into this:

blank <= false when h_cnt <= HRES and v_cnt <= VRES, else false;
-- And while I'm at it
h_sync <= '0' when h_cnt > HRES+HFP and h_cnt < HRES+HFP+HSP, else '1';

But I end up with a syntax error. Isn't there any way of branching test on inequalities such as greater than / less than outside of processes?

Modified code Inside the architecture:

-- blank is true outside of the visible area (where synchronisation pulses are sent and black is needed)
blank <= false when h_cnt <= HRES and v_cnt <= VRES else true;

-- Address of the pixel (block of memory containing the color of the pixel)
buffer_addr <= std_logic_vector(to_unsigned(h_cnt+v_cnt*HRES, buffer_addr'length)) 
                   when blank = false else (others => '0');

-- Colors returned correspond to the aimed pixel, but black if out of the visible area
R <= buffer_data(RDEPTH+GDEPTH+BDEPTH-1 downto GDEPTH+BDEPTH) when blank = false else (others => '0');
G <= buffer_data(GDEPTH+BDEPTH-1 downto BDEPTH) when blank = false else (others => '0');
B <= buffer_data(BDEPTH-1 downto 0) when blank = false else (others => '0');

-- Synchronisation pulses
h_sync <= '0' when h_cnt > HRES+HFP and h_cnt < HRES+HFP+HSP else '1';
v_sync <= '0' when v_cnt > VRES+VFP and v_cnt < VRES+VFP+VSP else '1';

-- Counter update procedure:
h_proc : process(pix_clk) 
begin
    if(rising_edge(pix_clk)) then
        -- counts the pixel clocks, update the line counts and reset the counters when appropriate
        if(h_cnt = HRES+HFP+HSP+HBP) then
            h_cnt <= 0;
            if(v_cnt = VRES+VFP+VSP+VBP) then
                v_cnt <= 0;
            else
                v_cnt <= v_cnt + 1; 
            end if;
        else
            h_cnt <= h_cnt + 1; 
        end if;
    end if;
end process;
\$\endgroup\$
4
\$\begingroup\$

Your approach is perfectly legal. The commas are not, however. Getting rid of them should solve your problem, unless there are other issues that are not clear from the code you posted.

Note that there are side-effects to moving your code out of a clocked process, though - the value of blank will change 1 clock earlier, and your timing performance will be worse. Make sure your design can sustain these changes.

Be aware also that the logic for h_sync is not equivalent (though this may actually be an improvement) - in the clocked process, h_sync is not assigned a value in every branch of the if-block, and it appears that it could get stuck at '0' for certain values of HFP, etc. In the concurrent assignment, its value is always explicitly assigned.

brief tutorial on sequential signal assignment

Refer to How does signal assignment work in a process?

The canonical way of thinking about VHDL processes is that signals are not updated until the process suspends (which, for a clocked process, is at the end of the process). It may be helpful to realize that clocked processes are meant to represent physical registers and actual signal propagation. Either way, if you are assigning multiple interdependent signals in clocked processes, the value can only "advance" one signal at a time on each clock edge.

So consider the following code:

(A)

process (clk)
  if rising_edge(clk) then
    foo <= foo + 1; -- if value is becoming N this clock cycle...
    if foo = N then -- value is still N-1 here - signal has not updated yet
      bar <= '1';
    else
      bar <= '0';
    end if;
  end if;
end process;

(B)

process (clk)
  if rising_edge(clk) then
    foo <= foo + 1; -- if value is becoming N this clock cycle...
  end if;
end process;

bar <= '1' when foo = N else '0'; -- assigned concurrently, so value is already N here

In (A), foo is not updated until the end of the process. On the clock cycle when foo becomes N, it hasn't been updated when the "if" check is done, so the value of bar will not change. bar will change 1 clock later, since on that iteration of the process, foo will have been updated to equal N.

In (B), bar is not assigned in a clocked process, and therefore will be updated as soon as foo changes (after some logic delays in the real world, of course).

Does that help?

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the quick answer. I was misled by the fact that the "with XXXX select XXXX when XXXX, XXXX when others" had a comma and syntax errors are so explicit... I uploaded the modified code. You spotted h_sync right, however in that particular code due to the front porch just before the pulse the waveform should have been correct (normally). \$\endgroup\$ – Mister Mystère Apr 21 '14 at 18:58
  • \$\begingroup\$ HOWEVER, I am puzzled about that way being worse for 'blank': I thought signals were changed at the end of the process, so still after one clock cycle? Because I understood that in a process 'foo <= foo + 1; if(foo = N) then ... end if;' would trigger the if an iteration after what's expected!? \$\endgroup\$ – Mister Mystère Apr 21 '14 at 19:01
  • \$\begingroup\$ I assumed h_sync probably wouldn't actually be in issue, but I thought I would mention it just in case. The change to timing performance and sequencing is the more significant one. \$\endgroup\$ – fru1tbat Apr 21 '14 at 19:04
  • 1
    \$\begingroup\$ I'm not saying it's better - there are several factors to consider. Above you said "signals [are] changed at the end of the process" - this is correct. Here you say it's "executed sequentially". These two statements are in conflict. See my attempted brief tutorial above for more information, and let me know what you're having trouble with. \$\endgroup\$ – fru1tbat Apr 21 '14 at 19:53
  • 1
    \$\begingroup\$ That's one way to say it, yes. I usually think of it as the signal assignment being "deferred" until the end of the process, as this makes visualizing actual hardware easier for me. Keep in mind that this is only true for signals, though; variables are updated immediately. \$\endgroup\$ – fru1tbat Apr 22 '14 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.