Question

I am currently trying to implement a data path, which calculates the following, in one clock cycle.

  • Takes input A and B and add them.
  • Shift the result of addition, one bit to right. (Dividing by 2)
  • Subtract the shifted result from another input C.

The behavioral architecture of the entity is simply shown below.

signal sum_out : std_logic_vector (7 downto 0);
signal shift_out : std_logic_vector (7 downto 0);

process (clock, data_in_a, data_in_b, data_in_c)
begin
    if clock'event and clock = '1' then
        sum_out <= std_logic_vector(unsigned(data_in_a) + unsigned(data_in_b));
        shift_out <= '0' & sum_out(7 downto 1);
        data_out <= std_logic_vector(unsigned(data_in_c) - unsigned(shift_out));
    end if;
end process;

When I simulate the above code, I do get the result I expect to get. However, I get the result, after 3 clock cycles, instead 1 as I wish. The simulation wave form is shown below.

Simulation Waveform

I am not yet familiar with implementing designs with timing concerns. I was wondering, if there are ways to achieve above calculations, in one clock cycle. If there are, how can I implement them?

Was it helpful?

Solution

Do do this with signals simply register only the last element in the chain (data_out). This analyzes, I didn't write a test bench to verify simulation.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity signal_single_clock is
    port ( 
        signal clock:       in  std_logic;
        signal data_in_a:   in  std_logic_vector(7 downto 0);
        signal data_in_b:   in  std_logic_vector(7 downto 0);
        signal data_in_c:   in  std_logic_vector(7 downto 0);
        signal data_out:    out std_logic_vector(7 downto 0)
    );
end entity;

architecture behave of signal_single_clock is
    signal sum_out : std_logic_vector (7 downto 0);
    signal shift_out : std_logic_vector (7 downto 0);
begin
    sum_out <= std_logic_vector(unsigned(data_in_a) + unsigned(data_in_b));
    shift_out <= '0' & sum_out(7 downto 1);
single_reg:
    process (clock)
    begin
        if clock'event and clock = '1' then
            data_out <= std_logic_vector(unsigned(data_in_c) - unsigned(shift_out));
        end if;
    end process;
end architecture;

OTHER TIPS

When you assign a new value to a signal inside a process, this new value will be available only after the process finishes execution. Therefore, anytime you read the signal's value you will be using the original value from when the process started executing.

On the other hand, assignments to varibles take place immediately, and the new value can be used in the subsequent statements if you wish.

So, to solve you problem, simply implement sum_out, shift_out, and data_out using variables, instead of signals. Then simply copy the value of data_out to an output port of your entity.

Without using variables:

sum <= in_a + in_b;
process (clock)
begin
    if rising_edge(clock) then
        data_out <= in_c - ('0' & sum(7 downto 1));
    end if;
end process;

All declarations except clock are unsigned(7 downto 0); why make it more complicated than that?

The original, pipelined to 3 cycles, will probably work at higher clock rates.

EDIT following comment:

I wanted to demonstrate that VHDL doesn't really have to be that verbose.

However there seem to be a lot of people "teaching" VHDL who are focussing on trivial elements and missing the big picture entirely, so I'll say a little bit about that.

VHDL is a strongly typed language, to prevent mistakes that creep in when types are mistaken for each other and (e.g.) you add two large numbers and get a negative result.

It does NOT follow from that, that you need type conversions all over the place. Indeed, if you need a lot of type conversions, it's a sign that your design is probably wrong, and it's time to rethink that instead of ploughing ahead down the wrong path.

Code - in ANY language - should be as clean and simple as possible.

Otherwise it's hard to read, and there are probably bugs in it.

The big difference between a C-like language and VHDL is this:

In C, using the correct data types you can write sum = in_a + in_b; and it will work. Using the wrong data types you can also write sum = in_a + in_b; and it will compile just fine; what it actually does is another matter! The bugs are hidden : it is up to you to determine the correct types, and if you get it wrong there is very little you can do except keep on testing.

in VHDL, using the right types you can write sum <= in_a + in_b; and using the wrong types, the compiler forces you to write something like sum <= std_logic_vector(unsigned(in_a) + unsigned(in_b)); which is damn ugly, but will (probably: see note 1) still work correctly.

So to answer the question : how do I decide to use unsigned or std_logic_vector?

I see that I need three inputs and an output. I could just make them std_logic_vectorbut I stop and ask: what do they represent?

Numbers.

Can they be negative? Not according to my reading of the specification (your question).

So, unsigned numbers... (Note 1)

Do I need non-arithmetic operations on them? Yes there's a shift.(Note 2)

So, numeric_std.unsigned which is related to std_logic_vector instead of natural which is just an integer.

Now you can't avoid type conversions altogether. Coding standards may impose restrictions such as "all top level ports must be std_logic_vector" and you must implement the external entity specification you are asked to; intermediate signals for type conversions are sometimes cleaner than the alternatives, e.g. in_a <= unsigned(data_in_a);
Or if you are getting instructions, characters and the numbers above from the same memory, for example, you might decide the memory contents must be std_logic_vector because it doesn't just contain numbers. But pick the correct place to convert type and you will find maybe 90% of the type conversions disappear. Take that as a design guideline.

(Note 1 : but what happens if C < (A+B)/2 ? Should data_out be signed? Even thinking along these lines has surfaced a likely bug that std_logic_vector left hidden...

The right answer depends on unknowns including the purpose of data_out : if it is really supposed to be unsigned, e.g. a memory address, you may want to flag an error instead of making it signed)

(Note 2 : there isn't a synthesis tool left alive that won't translate signal a : natural; ... x <= a/2 into a shift right, so natural would also work, unless there were other reasons to choose unsigned. A lot of people seem to still be taught that integers aren't synthesisable, and that's just wrong.)

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