How to convert 8 bits to 16 bits in VHDL? - vhdl

I have an input signal from ADC convertor that is 8 bits (std_logic_vector(7 downto 0)). I have to convert them to a 16 bits signal (std_logic_vector(15 downto 0)) for 16 bits signal processing to the 16 bits system.

If the 8 bit value is interpreted as signed (2's complement), then the general and standard VHDL conversion method is to use the IEEE numeric_std library:
library ieee;
use ieee.numeric_std.all;
architecture sim of tb is
signal slv_8 : std_logic_vector( 8 - 1 downto 0);
signal slv_16 : std_logic_vector(16 - 1 downto 0);
begin
slv_16 <= std_logic_vector(resize(signed(slv_8), slv_16'length));
end architecture;
So first the std_logic_vector is converted to a signed value, then the resize is applied, which will sign extend the signed value, and the result is finally converted back to std_logic_vector.
The conversion is rather lengthy, but has the advantage that it is general and works even if the target length is changed later on.
The attribute 'length simply returns the length of the slv_16 std_logic_vector, thus 16.
For unsigned representation instead of signed, it can be done using unsigned instead of signed, thus with this code:
slv_16 <= std_logic_vector(resize(unsigned(slv_8), slv_16'length));

architecture RTL of test is
signal s8: std_logic_vector(7 downto 0);
signal s16: std_logic_vector(15 downto 0);
begin
s16 <= X"00" & s8;
end;

This handles the conversion without having to edit the widths of the zeroes if either std_logic_vector changes:
architecture RTL of test is
signal s8: std_logic_vector(7 downto 0);
signal s16: std_logic_vector(15 downto 0) := (others => '0');
begin
s16(s8'range) <= s8;
end;

For completeness, yet another way which is occasionally useful:
-- Clear all the slv_16 bits first and then copy in the bits you need.
process (slv_8)
begin
slv_16 <= (others => '0');
slv_16(7 downto 0) <= slv_8;
end process;
I've not had to do this for vectors that I can recall, but I have had need of this under more complex circumstances: copying just a few relevant signals into a bigger, more complex, record was one time.

With the newly released VHDL-2019 standard you can do
larger_vec <= extend(shorter_vec);
where extend is a function defined as follows
function extend(vec : std_logic_vector) return target_vec of std_logic_vector is
variable result : std_logic_vector(target_vec'length - 1 downto 0) := (others => '0');
begin
assert vec'length <= target_vec'length report "Cannot extend to shorter vector";
result(vec'length - 1 downto 0) := vec;
return result;
end function;
Tool support is still a bit limited but at least one simulator supports this (Riviera-PRO).

Related

VHDL: big slv array slicing indexed by integer (big mux)

I want to slice a std_logic_vector in VHDL obtaining parts of it of fixed dimensions.
The general problem is:
din N*M bits
dout M bits
sel clog2(N) bits
Expected behaviour in an example (pseudocode): input 16 bit, want to slice it in 4 subvectors of 4bit each.
signal in: std_logic_vector(N*M-1 downto 0);
signal sel: integer;
-- with sel = 0
output <= in(N-1:0);
--with sel = 1 output <= in(2N-1:N)
-- with sel = 2
output <= in(3N-1:2N)
.....
--with sel = M-1
output <= in(M*N-1:(M-1)N)
I know a couples of way to do this, but I don't know which one is the best practice and give the best results in synthesis.
the entity
din: in std_logic_vector(15 downto 0);
dout: out std_logic_vector(3 downto 0);
sel: in std_logic_vecotor(1 downto 0)
CASE STATEMENT
case sel is
when "00" => dout <= din(3:0);
when "01" => dout <= din(7:4);
when "10" => dout <= din(11:8);
when "11" => dout <= din(15:12);
when others => ....`
It clearly implement a mux, but it's not generic at all and If the input gets big it's really hard to write and to codecover.
INTEGER INDEXING
sel_int <= to_integer(unsigned(sel));
dout <= din(4*(sel_int+1) - 1 downto 4*sel_int);
Extremely easy to write and to mantain, BUT it can have problems when the input is not a power of 2. For example, if I want to slice a 24bit vector in chunks of 4, what happen when the integer conversion of sel brings to the index 7?
A STRANGE TRADEOFF
sel_int <= to_integer(unsigned(sel));
for i in 0 to 4 generate
din_slice(i) <= din(4*(i+1)-1 downto 4*i);
end generate dout <= din_slice(sel_int);
I'm searching a solution that is general enough to be used with various input/output relationships and safe enough to be synthesized consistently everytime.
The Case statement is the only one with the Others case (that feels really safe), the other solutions rely on the slv to integer conversion and indexing that feels really comfortable but not so reliable.
Which solution would you use?
practical usecase
I have a 250bit std_logic_vector and I need to select 10 contigous bits inside of it starting from a certain point from 0 to 239. How can I do that in a way that is good for synthesis?
There is another option that is accepted by tools that allow VHDL 2008 (which includes Vivado and Prime Pro). You can use an unconstrained 2d type from a package:
type slv_array_t is array(natural range <>) of std_logic_vector; --vhdl 2008 unconstrained array type
then you can simply select which port you want. And it is as generic as you like.
library ieee;
use ieee.std_logic_1164.all;
use work.my_pkg.all;
entity mux is
generic (
N : natural;
M : natural
);
port (
sel : in natural;
ip : in slv_array_t (N-1 downto 0)(M-1 downto 0);
op : out std_logic_vector (M-1 downto 0);
);
end entity;
architecture rtl of mux is
begin
op <= ip(sel);
end architecture;
First you must extend the incoming data to be sure to have always as much bits as you need for connecting all multiplexer inputs (see the code below, process p_extend).
This will not create any logic at synthesis.
Second you must convert the resulting vector into an array, which you can access later by an index (see the code below, process p_create_array).
Again this will not create any logic at synthesis.
At last you must access this array by the select input signal (see the code below, process p_mux).
library ieee;
use ieee.std_logic_1164.all;
entity mux is
generic (
g_data_width : natural := 250;
g_slice_width : natural := 10;
g_sel_width : natural := 5;
g_start_point : natural := 27
);
port (
d_i : in std_logic_vector(g_data_width-1 downto 0);
sel_i : in std_logic_vector(g_sel_width-1 downto 0);
d_o : out std_logic_vector(g_slice_width-1 downto 0)
);
end entity mux;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
architecture struct of mux is
signal data : std_logic_vector(g_slice_width * 2**g_sel_width-1 downto 0);
type t_std_logic_slice_array is array (natural range <>) of std_logic_vector(g_slice_width-1 downto 0);
signal mux_in : t_std_logic_slice_array (2**g_sel_width-1 downto 0);
begin
p_extend: process(d_i)
begin
for i in 0 to g_slice_width * 2**g_sel_width-1 loop
if i+g_start_point<g_data_width then
data(i) <= d_i(i+g_start_point);
else
data(i) <= '0';
end if;
end loop;
end process;
p_create_array: process (data)
begin
for i in 0 to 2**g_sel_width-1 loop
mux_in(i) <= data((i+1)*g_slice_width-1 downto i*g_slice_width);
end loop;
end process;
p_mux: d_o <= mux_in(to_integer(unsigned(sel_i)));
end architecture;

VHDL - GHDL Initialise std_logic_vector with smaller bit length

I have a signal dataIn : std_logic_vector ( 15 downto 0);
I want to give an input less than 16-bits for example dataIn <= x"000a" and those bits occupy the most significant bits and the rest to be zero.
In verilog you can do that very easy but in VHDL you get the error:
"string length does not match that of the anonymous integer subtype defined t... ".
I know that if you use 16x"bit_string" solves the problem but this is only for VHDL-2008 and ghdl doesn't support yet VHDL-2008.
Are there any method for IEEE Std 1076-2002?
For VHDL-87/93/2002 you could use the resize function from the numeric_std package.
library ieee;
use ieee.numeric_std.all;
...
constant FOO : std_logic_vector(2 downto 0) := "010";
signal dataIn : std_logic_vector(15 downto 0) := std_logic_vector(resize(unsigned(FOO), 16));
Note that the resize function is only defined for types signed and unsigned.
If you want the short bit string to be placed into the MSBs you may need to use the 'reverse_order attribute.
Often you will find it easier to define a dedicated function which encapsulates more complicated initializations.
constant FOO : std_logic_vector(2 downto 0) := "010";
function init_dataIn (bar : std_logic_vector; len : integer) return std_logic_vector is
begin
return bar & (len - bar'length - 1 downto 0 => '0');
end function init_dataIn;
signal dataIn : std_logic_vector(15 downto 0) := init_dataIn(FOO, 16);

Use of conv_integer in VHDL

I am in the process of trying to write some code that will simply just shift a 32 bit vector left or right, with a 5 bit input that will be used for the shift amount (shamt). The issue I am having is trying to convert an std_logic_vector to an integer. My code is this:
library ieee;
use ieee.STD_LOGIC_1164.all;
use ieee.STD_LOGIC_ARITH.all;
entity shiftlogical is
port(x : in std_logic_vector(31 downto 0);
shamt : in std_logic_vector( 4 downto 0);
y : out std_logic_vector(31 downto 0));
end shiftlogical;
architecture beh of shiftlogical is
signal shift : integer;
signal temp : std_logic_vector(31 downto 0);
begin
shift <= conv_integer(unsigned(shamt));
temp <= x(shift downto 0);
y <= temp;
end beh;
The code is not complete I know, but to test some ideas I am trying to pass "00010" (2) into shamt, but shift comes out to be -2147483648. But I cannot figure out why it is doing this, nor can I find any resources online that shows anything different than what I am doing. I greatly appreciate any help.
-2147483648 (-2**31) is the default initial value for integers, being the leftmost, most negative value in its range. It suggests that the signal assignment to shift has not executed. Most likely because it is a continuous assignment and there hasn't been an event on shamt to cause it to update.
std_logic_arith is not an IEEE standard library. You should use to_integer() from ieee.numeric_std instead. It is also beneficial to keep numeric ports as unsigned or signed so that your intent is clear and to minimize type conversions. Also, you cannot directly assign the variable length slice of x to temp since their lengths do not match. You should use resize() (from numeric_std) to extend the length back to 32-bits or rethink your approach.
I fixed the obvious typo in the entity name, started the simulation (ModelSim) and forced the signal shamt to "00010". Then just after trying to run for 1 ps, ModelSim complains about:
Fatal: (vsim-3420) Array lengths do not match. Left is 32 (31 downto 0). Right is 0 (-2147483648 downto 0 (null array)).
Time: 0 ps Iteration: 0 Process: /shiftlogical/line__16 File: shiftlogical.vhdl
Fatal error in Architecture beh at shiftlogical.vhdl line 16
That is because all your concurrent statements are executed in parallel. The new signal values are scheduled for the next delta cycle within the simulation. Thus, the line
temp <= x(shift downto 0);
is executed with the old value of shift which is the initial value of this signal. The initial value of an integer is -2**31 as also Kevin pointed out.
Of course you can initialize the signal shift, but the only value which will not result in an error will be 31 because in this asignment the signal on the left and the expression on the right must match in array (std_logic_vector) size. The signal shamt must be forced to "11111" as well, so that shift keeps 31.
You cannot easily fix this, because for a left shift you must add zeros at the right (LSB) and for a right shift zeros or the sign at the left (MSB).
#Martin Zabel what I had really tested there was to see if shift would hold an integer value which it did until I tried to pass it in for temp <= x(shift downto 0); What I realized was that the signal needed to really be a variable to work as intended and as follows my code consists of:
library ieee;
use ieee.STD_LOGIC_1164.all;
use ieee.STD_LOGIC_ARITH.all;
entity shiftlogical is
port(x: in std_logic_vector(31 downto 0);
shamt: in std_logic_vector(4 downto 0);
dir: in std_logic;
y: out std_logic_vector(31 downto 0));
end shiftlogical;
architecture beh of shiftlogical is
begin
process(dir)
variable shift : integer;
begin
shift := conv_integer(unsigned(shamt));
if(dir = '0') then --Left shift
y(31 downto shift) <= x(31-shift downto 0);
y(shift downto 0) <= (others => '0');
elsif(dir = '1') then --Right shift
y(31-shift downto 0) <= x(31 downto shift);
y(31 downto 31-shift) <= (others => '0');
else --Always left shift
y(31 downto shift) <= x(31-shift downto 0);
y(shift downto 0) <= (others => '0');
end if;
end process;
end beh;

How does a 32:8 mux work?

I'm programming a 32:8 mux in vhdl.
The task is based on a exam question that oviously can be interpreted several ways. The origianal task was to: Program a 32:8 mux, using "with select when", and "if then else". I did'nt get how the 32:8 mux would work, so I did'nt know where to start, but thanks to you guys I figured I would program it as a 4:1 mux passing 4 groups of 8 bits.
This is what I got:
library ieee;
use ieee.std_logic_1164.all;
entity mux_using_with is
port (
input :in std_logic_vector (31 downto 0);
sel :in std_logic_vector (1 downto 0);
mux_out :out std_logic_vector (7 downto 0));
end entity;
architecture behavior of mux_using_with is
begin
with (sel) select
mux_out <= input(7 downto 0) when '00',
input(15 downto 8) when '01',
input(23 downto 16) when '10',
input(31 downto 24) when others;
end architecture;
And for the if version:
library ieee;
use ieee.std_logic_1164.all;
entity mux_using_if is
port (
input :in std_logic_vector (31 downto 0);
sel :in std_logic_vector (1 downto 0);
mux_out :out std_logic_vector (7 downto 0));
end entity;
architecture behavior of mux_using_if is
begin
MUX:
process (sel, input) begin
if (sel = '00') then
mux_out <= input(7 downto 0);
elsif (sel = '00') then
mux_out <= input(15 downto 8);
elsif (sel = '00') then
mux_out <= input(23 downto 16);
else
mux_out <= input(31 downto 24);;
end if;
end process;
end architecture;
Have I made any obvious mistakes?
A 32:8 selects 8 bits from 32 bits. There are two obvious ways to do this (and other less obvious ones):
Select any contiguous group of 8 bits starting from any point in the 32-bit input, with possible wrap-around. This is actually a 'barrel shifter'. There are 32 'start' positions, so you need a 5-bit selector; or
Select one of the 4 groups [7:0], [15:8], [23:16], or [31:24]. In this case, you need a 2-bit selector to select one of these groups.
I'm going to assume you want (2), since you've got a 2-bit selector. So, each of your 8 output bits is actually a 4:1 mux. Bit 0 of the output selects from either bit 0, 8, 16, or 24 of the input, for example, depending on the state of your 2-bit selector.
There are 4 straightforward ways to do this in VHDL: a sequential case statement, a selected signal assignment, direct logic, or array element selection. There are examples of all 4 styles on the Maia site. These only have a 1-bit output, so your task is to turn it into an 8 bit output. Have a look and let us know if you have a problem.

Adding Even Parity bit and 2 stop bits to a 8 bits std_logic_vector

Here is the code: In this the calculation for the parity bit is not done. Parity bit can be calculated using the for loop but is there any other short or better way to calculate the even parity bit in this context.
Is it somehow possible to use arrays instead of 8 TxDataReg std_logic_vector considering that after making arrays I wish to access bit by bit the array of 8 signals of 8 bits, bit by bit for sending the data in the uart_tx port?
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.NUMERIC_STD.ALL;
entity Uart_tx is
Port (
tx_clk_in : in STD_LOGIC;
reset : in STD_LOGIC;
tx : out STD_LOGIC;
Rx_Data_in : in STD_LOGIC_VECTOR(63 downto 0)
);
end Uart_tx;
architecture Behavioral of Uart_tx is
signal Tx_Data : STD_LOGIC_VECTOR(63 downto 0) := "00000000";
signal DataByteArray1 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray2 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray3 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray4 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray5 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray6 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray7 : std_logic_vector(7 downto 0) := (others => "00000000");
signal DataByteArray8 : std_logic_vector(7 downto 0) := (others => "00000000");
signal TxDataReg1 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg2 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg3 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg4 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg5 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg6 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg7 : std_logic_vector(10 downto 0) := (others => "00000000");
signal TxDataReg8 : std_logic_vector(10 downto 0) := (others => "00000000");
signal count : unsigned(2 downto 0) := (others => '0');
signal one_bit : std_logic := '0';
begin
Tx_Data <= Rx_Data_in;
DataByteArray1 <= Rx_Data_in(7 downto 0);
DataByteArray2 <= Rx_Data_in(15 downto 8);
DataByteArray3 <= Rx_Data_in(23 downto 16);
DataByteArray4 <= Rx_Data_in(31 downto 24);
DataByteArray5 <= Rx_Data_in(39 downto 32);
DataByteArray6 <= Rx_Data_in(47 downto 40);
DataByteArray7 <= Rx_Data_in(55 downto 48);
DataByteArray8 <= Rx_Data_in(63 downto 56);
Process (tx_clk_in)
begin
-- Calculate the parity bit
for i in 0 to 7 loop
one_bit = DataByteArray1(i);
if one_bit = '1' then
count = count + 1;
end if;
end loop;
-- For all the registers,one even parity & two stop bits I am trying to add in the end
if count mod 2 = 0 then
TxDataReg1 <= DataByteArray1&'0'&'11'; -- I am not so sure that this works or not
count <= "000";
else
TxDataReg1 <= DataByteArray1&'1'&'11';
count <= "000";
end if;
-- Send the uart data from TxDataReg1,TxDataReg2 ...
-- etc.
end process;
end behavioral;
This UART would be much easier to understand if you created a State Machine. State Machines give your code an organized flow. The flow just makes more sense. In VHDL you can create enumerated states which means that you can give them names. I recommend this approach.
It's much harder to keep counters throughout your design to know exactly when to insert the parity bit or when to insert the 2 stop bits in your UART design. If you have a nice state machine it will make much more sense to you I believe. This is especially recommended for anyone new at FPGAs.
When you calculate your parity, just keep a running parity bit that gets an XOR with the outgoing serial data. Create a state to insert your parity bit at the correct time, then insert your two stop bits.
For an example of this, look at this UART VHDL Code
I would second the suggestion to reorganize this to use an FSM that works on just a byte at a time. Then you will have a general purpose async. TX entity that another controller can send bytes to as needed.
As to managing your data. It would be simpler if you created an array of byte arrays:
subtype byte is std_logic_Vector(7 downto 0);
type byte_array is array(natural range <>) of byte;
signal data_byte_array : byte_array(1 to 8);
signal byte_index : unsigned(2 downto 0);
...
-- Select the current byte
cur_byte <= data_byte_array(to_integer(byte_index));
The subtype isn't strictly necessary but it is a good habit to use for common data types to save you from littering your code with so many hard-coded array bounds.
For calculating parity you need to adopt the hardware mindset of implementing logic gates rather than the software approach of counting set bits. Parity calculation boils down to an XOR-reduce operation applied to all the bits in your vector. For even parity, you XOR all bits. For odd parity, you XOR all bits and invert the result. Because XOR is equivalent to a controlled inversion you can select the parity type by setting an initial state and performing one extra XOR to get the optional inversion based on your desire for odd or even.
-- Any VHDL:
variable parity : std_logic;
parity := '0'; -- Set to '1' to get odd parity
for i in cur_byte'range loop
parity := parity xor cur_byte(i);
end loop;
-- VHDL-2002
use ieee.reduce_pack.xor_reduce;
parity := xor_reduce(cur_byte);
-- VHDL-2008
parity := xor cur_byte;
In synthesis these approaches all boil down to the same logic so any of them is fine for all practical purposes. This is an explicitly parallel operation and you don't have to step through the byte bitwise with the unneeded overhead of a counter.
You have committed a cardinal sin of mixing the non-standard Synopsys libraries std_logic_unsigned, _signed, and _arith with the true standard numeric library numeric_std. Never mix them in the same file and, better yet, never use the Synopsys libraries at all. They are a historical aberration best forgotten.

Resources