So here's the problem. I've written code for a binary divider that should output 7-bit 7 segment display binary code to go into an 8 x 7segment display. (2 7segments for dividend,divisor,quotient,remainder each and in that order). This 8 x 7segment display on my dev-board has one 7-bit input(a to g) and a 3-bit select.
So the basic idea is I have to output the dividend,divisor,quotient and remainder sequentially, continuously and fast enough such that to the human eye the output looks constant despite the fact that the each of the eight 7 segments is being enabled one by one according to what my output is.
Originally, the divider gives all the outputs (dividend,divisor,quotient,remainder)in binary which are then converted by a function to 8-bit bcd and that bcd number is then broken down into two 4-bit bcd numbers by another function(Now I have 8 output variables: 2 representing dividend,2 representing divisor etc).These 4-bit numbers are converted by another function to 7 segment.
Here is the full code:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
entity division is
generic(SIZE: INTEGER := 8);
port(reset: in STD_LOGIC; --reset
en: in STD_LOGIC; --enable
clk: in STD_LOGIC; --clock
num: in STD_LOGIC_VECTOR((SIZE - 1) downto 0); --dividend
den: in STD_LOGIC_VECTOR((SIZE - 1) downto 0); --divisor
whatgoes:out STD_LOGIC_VECTOR(6 downto 0) --output
);
end division;
architecture behav of division is
signal bufreg: STD_LOGIC_VECTOR((2 * SIZE - 1) downto 0); --signal array to hold both accumulator and dividend registers as one i.e bufreg(18 bits)
signal dbuf: STD_LOGIC_VECTOR((SIZE - 1) downto 0); --signal array to hold the divisor
signal count: INTEGER range 0 to SIZE; --count to determine when to stop
signal MYcount: INTEGER range 0 to 100;
signal res: STD_LOGIC_VECTOR((SIZE - 1) downto 0); --result/quotient
signal rm : STD_LOGIC_VECTOR((SIZE - 1) downto 0); --remainder
alias ADreg is bufreg((2 * SIZE - 1) downto SIZE); --ADreg is is alias for top half of bufreg register(17th to 9th bit)
alias DVNDreg is bufreg((SIZE - 1) downto 0); --DVNDreg is is alias for bottom half of bufreg register(8th to 0th bit)
--Function definitions
function to_bcd ( bin : std_logic_vector(7 downto 0) ) return std_logic_vector; --converts 8 bit binary to 8 bit BCD
function m7seg (bin : std_logic_vector(3 downto 0) ) return std_logic_vector; --converts 4 bit BCD to 7 bit 7segment
function breakdown1 ( bin : std_logic_vector(7 downto 0) ) return std_logic_vector; --breaks an 8 bit BCD into a 4 bit BCD with lower bits
function breakdown2 ( bin : std_logic_vector(7 downto 0) ) return std_logic_vector; ----breaks an 8 bit BCD into a 4 bit BCD with higher bits
--this function assigns the first 4 bits of an 8 bit BCD number to a 4-bit vector
function breakdown1 ( bin : std_logic_vector(7 downto 0) ) return std_logic_vector is
variable bint : std_logic_vector(3 downto 0) :=bin(3 downto 0);
begin
return bint;
end breakdown1;
--this function assigns the last 4 bits of an 8 bit BCD number to a 4-bit vector
function breakdown2 ( bin : std_logic_vector(7 downto 0) ) return std_logic_vector is
variable bint : std_logic_vector(3 downto 0) :=bin(7 downto 4);
begin
return bint;
end breakdown2;
--This function converts 8 bit binary to 8 bit BCD
function to_bcd ( bin : std_logic_vector(7 downto 0) ) return std_logic_vector is
variable i : integer:=0;
variable bcd : std_logic_vector(7 downto 0) :=(others => '0');
variable bint : std_logic_vector(7 downto 0) :=bin;
variable bcd2 : std_logic_vector(7 downto 0) :=(others => '0');
begin
for i in 0 to 7 loop -- repeating 8 times.
bcd(7 downto 1) := bcd(6 downto 0); --shifting the bits.
bcd(0) := bint(7);
bint(7 downto 1) := bint(6 downto 0);
bint(0) :='0';
if(i < 7 and bcd(3 downto 0) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(3 downto 0) := bcd(3 downto 0) + "0011";
end if;
if(i < 7 and bcd(7 downto 4) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(7 downto 4) := bcd(7 downto 4) + "0011";
end if;
--if(i < 7 and bcd(11 downto 8) > "0100") then --add 3 if BCD digit is greater than 4.
--bcd(11 downto 8) := bcd(11 downto 8) + "0011";
--end if;
end loop;
bcd2(7 downto 0):=bcd(7 downto 0);
return bcd2;
end to_bcd;
--This function converts 4 bit bcd to 7 segment
function m7seg (bin : std_logic_vector(3 downto 0))return std_logic_vector is
variable bint : std_logic_vector(3 downto 0):=bin(3 downto 0);
variable out7 : std_logic_vector(6 downto 0);
begin
case bint is
when "0000"=> out7:="1111110";
when "0001"=> out7:="0110000";
when "0010"=> out7:="1101101";
when "0011"=> out7:="1111001";
when "0100"=> out7:="0110011";
when "0101"=> out7:="1011011";
when "0110"=> out7:="X011111";
when "0111"=> out7:="1110000";
when "1000"=> out7:="1111111";
when "1001"=> out7:="111X011";
when others=> out7:="0000000";
end case;
return out7;
end m7seg;
begin
--our process begins here (shift and subtract/ Non restoring division)
p_001: process(reset, en, clk, bufreg)
begin
if reset = '1' then
res <= (others => '0');
rm <= (others => '0');
dbuf <= (others => '0');
bufreg <= (others => '0');
count <= 0;
MYcount <= 1;
elsif rising_edge(clk) then
if en = '1' then
case count is
when 0 =>
ADreg <= (others => '0');
DVNDreg <= num;
dbuf <= den;
res <= DVNDreg;
rm <= ADreg;
count <= count + 1;
when others =>
if bufreg((2 * SIZE - 2) downto (SIZE - 1)) >= dbuf then
ADreg <= '0' & (bufreg((2 * SIZE - 3) downto (SIZE - 1)) - dbuf((SIZE - 2) downto 0));
DVNDreg <= DVNDreg ((SIZE - 2) downto 0) & '1';
else
bufreg <= bufreg((2 * SIZE - 2) downto 0) & '0';
end if;
if count /= SIZE then
count <= count + 1;
else
count <= 0;
end if;
end case;
end if;
res <= DVNDreg;
rm <= ADreg;
MYcount<=MYcount+1;
whatgoes<=(others => '0');
case MYcount is
when 2 =>
whatgoes<=m7seg(breakdown1(to_bcd(rm))); --first 7segment(lower bits of remainder)
when 3 =>
whatgoes<=m7seg(breakdown2(to_bcd(rm))); --second 7segment (higher bits of remainder)
when 4 =>
whatgoes<=m7seg(breakdown1(to_bcd(res))); --third 7segment (lower bits of result/quotient)
when 5 =>
whatgoes<=m7seg(breakdown2(to_bcd(res))); --fourth 7segment (higher bits of result/quotient)
when 6 =>
whatgoes<=m7seg(breakdown1(to_bcd(den))); --fifth 7segment (lower bits of divisor)
when 7 =>
whatgoes<=m7seg(breakdown2(to_bcd(den))); --sixth 7segment (higher bits of divisor)
when 8 =>
whatgoes<=m7seg(breakdown1(to_bcd(num))); --seventh 7segment (lower bits of number/dividend)
when 9 =>
whatgoes<=m7seg(breakdown2(to_bcd(num))); --eigth 7segment (higher bits of number/dividend)
when 10 =>
MYcount<=1;
when others =>
NULL;
end case;
end if;
end process;
end behav;
When I try to run a simulation, it gives me all kinds of funky stuff. I want the output (whatgoes(6 downto 0)) to change with the rising edge of the clock(clk). The problem is that since I'm a beginner at VHDL,Ive been having a lot of problems with synthesizing sequential statements.
Inside the process p_001 with enable, clock, and reset in the sensitivity list, i put this case statement. It executes on a positive edge condition.
Code extract:
case MYcount is
when 2 =>
whatgoes<=m7seg(breakdown1(to_bcd(rm))); --first 7segment(lower bits of remainder)
when 3 =>
whatgoes<=m7seg(breakdown2(to_bcd(rm))); --second 7segment (higher bits of remainder)
when 4 =>
whatgoes<=m7seg(breakdown1(to_bcd(res))); --third 7segment (lower bits of result/quotient)
when 5 =>
whatgoes<=m7seg(breakdown2(to_bcd(res))); --fourth 7segment (higher bits of result/quotient)
when 6 =>
whatgoes<=m7seg(breakdown1(to_bcd(den))); --fifth 7segment (lower bits of divisor)
when 7 =>
whatgoes<=m7seg(breakdown2(to_bcd(den))); --sixth 7segment (higher bits of divisor)
when 8 =>
whatgoes<=m7seg(breakdown1(to_bcd(num))); --seventh 7segment (lower bits of number/dividend)
when 9 =>
whatgoes<=m7seg(breakdown2(to_bcd(num))); --eigth 7segment (higher bits of number/dividend)
when 10 =>
MYcount<=1;
when others =>
NULL;
end case;
I'm pretty sure my problem lies here since the rest of my code works fine.
I apologize for uploading such a convoluted mess of code. I'm genuinely stuck and I've been at this for a good number of hours.
Any help would be greatly appreciated. I know it takes a special kind of devotion and patience to answer such a long,boring and nooby problem.
But to whoever can help or provide a link to something that has an answer to my kind of problem, you'd have done me a great service.
I'm using ISE 14.3and iSim.
So, thanks to rick, I solved this.
He helped me realize that I was forgetting to drive the 3-bit select output. As it turns out, driving it using a case statement and counting variable solved my problem of executing the code sequentially.
I know the code is not exactly written in an organized way but i hope with time i'll get better.
process (clk,tmp,rm,res,den,num)
variable CLR: boolean:=true;
begin
if (CLR=true) then
tmp <= "000";
CLR:=false;
elsif (clk'event and clk='1') then
tmp <= tmp + 1;
if tmp<=8 then
CLR:=true;
end if;
end if;
case tmp is
when "000" =>
whatgoes<=m7seg(breakdown1(to_bcd(rm))); --first 7segment(lower bits of remainder)
when "001" =>
whatgoes<=m7seg(breakdown2(to_bcd(rm))); --second 7segment (higher bits of remainder)
when "010" =>
whatgoes<=m7seg(breakdown1(to_bcd(res))); --third 7segment (lower bits of result/quotient)
when "011" =>
whatgoes<=m7seg(breakdown2(to_bcd(res))); --fourth 7segment (higher bits of result/quotient)
when "100" =>
whatgoes<=m7seg(breakdown1(to_bcd(den))); --fifth 7segment (lower bits of divisor)
when "101" =>
whatgoes<=m7seg(breakdown2(to_bcd(den))); --sixth 7segment (higher bits of divisor)
when "110" =>
whatgoes<=m7seg(breakdown1(to_bcd(num))); --seventh 7segment (lower bits of number/dividend)
when "111" =>
whatgoes<=m7seg(breakdown2(to_bcd(num))); --eigth 7segment (higher bits of number/dividend)
when others =>
NULL;
end case;
sel<=tmp;
end process;
I'm basically shooting in the dark here; maybe if you post a simulation picture it will help us understand your problem better. Anyway, since we're at it, why not talk about a few random issues:
The code would be easier to understand (and to work with) if you'd split it into a few blocks, each with a single purpose. You could have one block do the division, and output only the quotient and remainder. Another block could take in 8 BCD values, and multiplex them so that they appear correctly on your board's displays. If we can concentrate on one part of the problem at a time, it will be easier to spot anything wrong.
You mention a 3-bit select on the LCD, but I don't see in your code where you drive it. Maybe you should output something based on your signal MYcount?
To make sure your functions are working ok, you could put them in a package and create a self-cheking testbench. At least that's how I'd do it. This would take that variable out of the equation.
Please post some simulation results so that we can help you out.
Related
I programmed an 8-bit shifter in vhdl:
entity 8b is
port(s, clk : in std_logic; p : out std_logic_vector (7 downto 0));
end entity;
architecture arch of 8b is
Signal iq : std_logic_vector (7 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
iq(7) <= s;
iq(6 downto 0) <= iq(7 downto 1);
end if;
end process;
p <= iq;
end architecture;
The idea is that I'm taking input and giving it to my first D-FF.
Then over the next 7 cycles, the other Flip Flops get the other serial inputs which will be given to the parallel output p.
However, I'm not sure if this logic is flawed because this is the solution we got for this exercise:
architecture behavior of 8b is
signal p_intern : std_logic_vector(7 downto 0);
begin
P <= p_intern;
process(CLK)
begin
if rising_edge(CLK) then
p_intern <= p_intern(6 downto 0) & S;
end if;
end process;
end architecture;
But I don't get the p_intern <= p_inter(6 downto 0) & S; part.
Can someone please explain the logic behind this and if my version is also valid?
The only difference between the two implementations seem to be the lines
iq(7) <= s;
iq(6 downto 0) <= iq(7 downto 1);
vs.
p_intern <= p_intern(6 downto 0) & S;
and that iq is named p_intern. Let's assume they are both named iq for the sake of comparison.
Let's see what they are doing:
The first implementation (yours) assigns to the positions of iq:
7 6 5 ... 1 0
s iq(7) iq(6) ... iq(2) iq(1)
The second implementation (the solution) assigns
7 6 5 ... 1 0
iq(6) iq(5) iq(4) ... iq(0) s
Where iq(6 downto 0) & s means "concatenate s to the right of iq(6 downto 0)".
So they are not equivalent. Your implementation shifts in the values from the left, and the solution shifts in the values from the right. Which one is correct depends on the specification (presumably the solution is correct).
I'm trying to implement an FSM with a RAM behavior. There are multiple addresses of this ram that should be initialized while describing this FSM. So, I'm using the array aggregation technique to initialize the first 20 addresses of the ram_block. However, I'm getting a bad syntax error on each line the aggregation has occurred or the partial section of the ram_block(i) has initialized. Any helps would be appreciated.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
entity RegisterController is
port(
r1_p: inout std_logic_vector(31 downto 0);
r2_p: inout std_logic_vector(31 downto 0);
write_p: in std_logic;
enable_p: in std_logic;
clk_p: in std_logic;
ram_rw: in std_logic; -- 0 => Read from ram | 1 => Write to the ram
reset_p: in std_logic
);
end RegisterController;
architecture RTL of RegisterController is
-- Create the ram word
subtype ram_word is std_logic_vector(66 downto 0);
-- Create the ram block of 32 ram_words
type ram_block is array (31 downto 0) of ram_word;
-- Address to read from the ram
signal R_ADDR_S: std_logic_vector(4 downto 0) := "00000";
begin
RAM_LOAD: process(clk)
begin
-- We're gonna load the 32 words of this ram with clock first
-- Outputs are being updated in the runtime :D
if(rising_edge(clk)) then
if(ram_rw = '1') then
-- STATE 0 DESCRIPTION
ram_block(0) <= ("000", std_logic_vector(to_unsigned(0, 32)), std_logic_vector(to_unsgined(0, 32)));
ram_block(1) <= ("000", std_logic_vector(to_unsigned(0, 32)), std_logic_vector(to_unsigned(0, 32)));
ram_block(2) <= ("001", std_logic_vector(to_unsigned(0, 32)), std_logic_vector(to_unsigned(0, 32)));
ram_block(3) <= ("001", std_logic_vector(to_unsigned(0, 32)), std_logic_vector(to_unsigned(0, 32)));
-- STATE 1 DESCRIPTION
ram_block(4) <= (66 downto 64) => "001";
ram_block(5) <= (66 downto 64) => "001";
ram_block(6) <= ("001", r2_p, r1_p);
ram_block(7) <= (66 downto 64) => "010";
-- STATE 2 DESCRIPTION
ram_block(8) <= (66 downto 64) => "010";
ram_block(9) <= (66 downto 64) => "010";
ram_block(10) <= (66 downto 64) => "011";
ram_block(11) <= (66 downto 64) => "011";
-- STATE 3 DESCRIPTION
ram_block(12) <= (66 downto 64) => "011";
ram_block(13) <= (66 downto 64) => "011";
ram_block(14) <= (66 downto 64) => "100";
ram_block(15) <= (66 downto 64) => "100";
-- STATE 4 DESCRIPTION
ram_block(16) <= (66 downto 64) => "100";
ram_block(17) <= (66 downto 64) => "100";
ram_block(18) <= (66 downto 64) => "001";
ram_block(19) <= (66 downto 64) => "001";
ram_block(31 downto 20) <= std_logic_vector(to_unsigned(0, 67));
end if;
end if;
end process;
START_FSM: process(clk)
-- TEMPORARY VARIABLE TO STORE THE READ VALUE FROM THE RAM BLOCK
variable temp_read_ram: std_logic_vector(66 downto 0);
variable temp_read_ram2: std_logic_vector(66 downto 0);
-- R3 Declaration as a variable
variable R3_V: std_logic_vector(31 downto 0);
begin
if(rising_edge(clk)) then
if(ram_rw = '0') then
-- START READING THE RAM FROM ADDRESS 0
temp_read_ram := ram_block(to_integer(unsigned(R_ADDR_S)));
R_ADDR_S(4 downto 2) <= temp_read_ram(66 downto 64);
R_ADDR_S(1 downto 0) <= (enable_p, write_p);
-- UPDATE THE OUTPUTS
if(R_ADDR_S = "00110") then
-- READ THE PREVIOUS VALUE IN THAT ADDRESS
temp_read_ram2 <= ram_block(R_ADDR_S);
-- UPDATE THE OUTPUT VALUES INSIDE RAM
ram_block(R_ADDR_S) <= (temp_read_ram2(66 downto 64), r2_p, r1_p);
-- NO NEED TO UPDATE r2_p and r1_p
elsif(R_ADDR_S = "00111") then
-- PUT THE CURRENT VALUE OF R1 TO THE R3
temp_read_ram2 <= ram_block(R_ADDR_S);
-- SAVE R1 TO THE R3_V
R3_V := temp_read_ram2(31 downto 0);
elsif(R_ADDR_S = "01110" or R_ADDR_S = "01111") then
-- READ THE PREVIOUS VALIE IN THOSE ADDRESSES
temp_read_ram2 <= ram_block(R_ADDR_S);
-- UPDATE THE OUTPUT VALUE OF R2 INSIDE RAM
ram_block(R_ADDR_S) <= (temp_read_ram2(66 downto 64), R3_V, temp_read_ram2(31 downto 0));
-- UPDATE THE OUTPUT VALUE OF r2_p
r2_p <= R3_V;
else
else
-- NO CHANGE
ram_block(R_ADDR_S) <= ram_block(R_ADDR_S);
end if;
end if;
end if;
end process;
end RTL;
There's not one, but many syntax errors in your code.
First off, ram_block is a type, not a signal. So why are you assigning values to it? You need to declare a signal first. I.e.
-- Create the ram block of 32 ram_words
type ram_block_type is array (31 downto 0) of ram_word;
signal ram_block : ram_block_type := (others => (others => '0'));
^ what I also include here, is initialization of the signal. Same can be done for:
signal R_ADDR_S: std_logic_vector(4 downto 0) := (others => '0');
In the process, you are assigning values to many locations in the RAM at the same time! That is not RAM-like. A random access memory usually only has 1 or 2 ports, accessing 1 resp. 2 elements at a time. You are designing generic distributed memory, so imho should not call it RAM.
Assigning to part of the array has a specific syntax. You can either assign the whole matrix in one go (this is VHDL-2008 by the way):
ram_block <= (4 => (66 downto 64 => "001", others => '0'), others => '0');
Note: you need to assign all values here, hence the others statement.
Second option, assign one sub-array
ram_block(4) <= (66 downto 64 => "001", others => '0');
Finally, and probably what you want, assign a specific sub-set of a sub-array:
ram_block(4)(66 downto 64) <= "001";
But in this case, you want to initialize the array, as else unassigned std_logic's will have the default value 'U'.
Then:
ram_block(31 downto 20) <= std_logic_vector(to_unsigned(0, 67));
This will not work. You're assigning an array structure to an array-of-arrays.
Variable R3_V is assigned in one situation, but not used directly: It is used a different clock cycle. In that case do not use a variable: that is bad coding style. It should be a signal.
And you must remember that signals assigned in a clocked process will not have access to their new value until the next delta cycle. Thus the assignment of signal R_ADDR_S(4 downto 2) <= temp_read_ram(66 downto 64); will not be available for the following if-statements!
Other things:
clk should probably be clk_p
to_unsgined -> to_unsigned
in line 79 you use the correct ram_block(to_integer(unsigned(R_ADDR_S))), but later (line 86) you do it wrong ram_block(R_ADDR_S)
in line 79 you also correctly assign a variable using :=, but later (line 86 again) you use the incorrect <=.
there's a double else at the end of the code.
I'm trying to make a 16bit to BCD conversion.
I have found this link for a 8 bit and I'm trying to convert it to 16 bits.
http://vhdlguru.blogspot.nl/2010/04/8-bit-binary-to-bcd-converter-double.html
I don't know what im doing wrong the rpm_1000 keeps changing and the rpm_100 stays at 4. Does anyone have a idea what i did wrong?
process (Hex_Display_Data)
variable i : integer:=0;
variable bcd : std_logic_vector(19 downto 0) := (others => '0');
variable bint : std_logic_vector(15 downto 0) := Hex_Display_Data;
begin
for i in 0 to 15 loop -- repeating 16 times.
bcd(19 downto 1) := bcd(18 downto 0); --shifting the bits.
bcd(0) := bint(15); -- shift bit in
bint(15 downto 1) := bint(14 downto 0); --removing msb
bint(0) :='0'; -- adding a '0'
if(i < 15 and bcd(3 downto 0) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(3 downto 0) := bcd(3 downto 0) + "0011";
end if;
if(i < 15 and bcd(7 downto 4) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(7 downto 4) := bcd(7 downto 4) + "0011";
end if;
if(i < 15 and bcd(11 downto 8) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(11 downto 8) := bcd(11 downto 8) + "0011";
end if;
if(i < 15 and bcd(15 downto 12) > "0100") then --add 3 if BCD digit is greater than 4.
bcd(15 downto 12) := bcd(15 downto 12) + "0011";
end if;
end loop;
rpm_1000 <= bcd(15 downto 12);
rpm_100 <= bcd(11 downto 8);
rpm_10 <= bcd(7 downto 4);
rpm_1 <= bcd(3 downto 0);
end process ;
Note four BCD digits can be wholly contained in 14 bits of input (your Hex_Display_Data) and unused bcd 'bits' (19 downto 16) will get eaten during synthesis along with all the add 3's that can't occur because their upper two bits are '0's (not > 4).
If you constrain your bcd value to 4 hex digits, and your loop iteration to 14 bits:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity bin2bcd is
port (
input: in std_logic_vector (15 downto 0);
ones: out std_logic_vector (3 downto 0);
tens: out std_logic_vector (3 downto 0);
hundreds: out std_logic_vector (3 downto 0);
thousands: out std_logic_vector (3 downto 0)
);
end entity;
architecture fum of bin2bcd is
alias Hex_Display_Data: std_logic_vector (15 downto 0) is input;
alias rpm_1: std_logic_vector (3 downto 0) is ones;
alias rpm_10: std_logic_vector (3 downto 0) is tens;
alias rpm_100: std_logic_vector (3 downto 0) is hundreds;
alias rpm_1000: std_logic_vector (3 downto 0) is thousands;
begin
process (Hex_Display_Data)
type fourbits is array (3 downto 0) of std_logic_vector(3 downto 0);
-- variable i : integer := 0; -- NOT USED
-- variable bcd : std_logic_vector(15 downto 0) := (others => '0');
variable bcd: std_logic_vector (15 downto 0);
-- variable bint : std_logic_vector(15 downto 0) := Hex_Display_Data;
variable bint: std_logic_vector (13 downto 0); -- SEE process body
begin
bcd := (others => '0'); -- ADDED for EVERY CONVERSION
bint := Hex_Display_Data (13 downto 0); -- ADDED for EVERY CONVERSION
for i in 0 to 13 loop
bcd(15 downto 1) := bcd(14 downto 0);
bcd(0) := bint(13);
bint(13 downto 1) := bint(12 downto 0);
bint(0) := '0';
if i < 13 and bcd(3 downto 0) > "0100" then
bcd(3 downto 0) :=
std_logic_vector (unsigned(bcd(3 downto 0)) + 3);
end if;
if i < 13 and bcd(7 downto 4) > "0100" then
bcd(7 downto 4) :=
std_logic_vector(unsigned(bcd(7 downto 4)) + 3);
end if;
if i < 13 and bcd(11 downto 8) > "0100" then
bcd(11 downto 8) :=
std_logic_vector(unsigned(bcd(11 downto 8)) + 3);
end if;
if i < 13 and bcd(15 downto 12) > "0100" then
bcd(11 downto 8) :=
std_logic_vector(unsigned(bcd(15 downto 12)) + 3);
end if;
end loop;
(rpm_1000, rpm_100, rpm_10, rpm_1) <=
fourbits'( bcd (15 downto 12), bcd (11 downto 8),
bcd ( 7 downto 4), bcd ( 3 downto 0) );
end process ;
end architecture;
Note the use of aliases to enable your names to be used in an existing otherwise compatible Minimal, Complete and Verifiable Example which your question did not provide.
Aggregate signal assignment is also taken from the original, your assignment to the individual digits should work just fine.
There are two changes besides limiting the conversion to 14 bits and the number of BCD digits to match the number of digits output.
The bcd and bint variables are now cleared every time the process is resumed (sensitive to updates to Hex_Display_Data). These were causing causing your otherwise unverifiable errors more than likely.
Extraneous parentheses have been removed.
You didn't supply context clauses. The code shown uses package numeric_std as opposed to the -2008 numeric_std_unsigned offering compatibility with earlier revisions of the standard while using IEEE authored packages.
You'll get something that works, provable with a testbench:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity bin2bcd_tb is
end entity;
architecture foo of bin2bcd_tb is
signal input: std_logic_vector (15 downto 0) := (others => '0');
signal ones: std_logic_vector (3 downto 0);
signal tens: std_logic_vector (3 downto 0);
signal hundreds: std_logic_vector (3 downto 0);
signal thousands: std_logic_vector (3 downto 0);
begin
DUT:
entity work.bin2bcd
port map (
input => input,
ones => ones,
tens => tens,
hundreds => hundreds,
thousands => thousands
);
STIMULUS:
process
begin
for i in 0 to 1001 loop
wait for 20 ns;
input <= std_logic_vector(to_unsigned(9999 - i, 16));
end loop;
wait for 20 ns;
wait;
end process;
end architecture;
Some other stimulus scheme can be used to toggle BCD digit roll over of all four digits.
This testbench provides input values starting at 9999 and decrementing 1001 times to show all four digits transitioning:
I can easily be modified to prove every transition of every BCD digit.
In summary the errors you were encountering appear to have come from the difference in elaboration for variables in a subprogram, where bcd and bint would be dynamically elaborated and initialized every function call, and in the process where they would be only initialized once.
From examining Xilinx's User Guide 901 Vivado Design Suite User Guide, Synthesis (2015.3), Chapter 4: VHDL Support, Combinatorial Processes, case Statements, for-loop Statements, the for loop appears to be supported for synthesis and has been reported to be synthesis eligible in other double dabble questions on stackoverflow. The issue would be support for repetitive assignment to variables in repeated sequences of sequential statements, which should be supported. There are is at least one other double dabble question on stackoverflow where successful synthesis had been reported using such a for loop.
Note that constraining the input value you deal with to 14 bits doesn't detect the effects of larger binary numbers (> 9999) which your process does not otherwise do either providing only 4 BCD output digits. You could deal with that by checking if the input value is greater than 9999 (x"270F").
The + 3 represents 1 LUT depth in an FPGA (4 bit input, 4 bit output), there are some number of them layered in depth based on the size of the converted number (the range of i). Allowing time for conversion propagation through ADD3's is offset by the rate at which the display can be visually interpreted. If you updated Hex_Display_Data in the millisecond range you likely could not tell the difference visually.
Running the loop 16 times will cause the value in the BCD registers to be multiplied by 65536 (mod 100000) and added to the value in the binary registers. Say the value is 4000. Then 4000x65536 yields 44000. 44000x65536 yields 84000. 84000x65536 yields 24000. 24000x65536 yields 64000. And 64000x65536 yields 4000.
To make the algorithm work, you must start out by clearing the BCD registers. It also wouldn't hurt to fix the comment about how many times your loop runs.
Incidentally, a practical implementation of a binary to BCD converter should generally accept a clock input, and perform one step for each active clock edge. If your VHDL is running entirely in simulation the complexity of the resulting logic won't matter, but trying to perform everything at once in real hardware will be rather expensive. By contrast, the hardware to do a simple shift of the binary number and a multiply-by-two of the BCD number will be much simpler. Note that if you do things "all at once", the most significant bit of the output will depend upon the second-least-significant bit of the input, meaning the input signal will have to propagate through all the logic in one step. By contrast, if you shift by one bit per clock cycle, each bit of the output will depend only upon at most four bits of the input (since each digit will be in the range 0-9 before the adjustment phase, adding 3 will never cause a carry out).
Also, the "double dabble" algorithm requires that the adjustment be performed before the BCD shifts, but it looks as though the code is performing the adjustment after. Doing the adjustment after is fine if one looks at bit
ranges 16..13, 12..9, 8..5, and 4..1 rather than 15..12, etc. Alternatively, one could specify that the value of bits 19..17 should be the value of bits 18..16, the value of bits 16..13 should be either the value of bits 15..12 (if less than 5) or the value of bits 15..12, plus three (if greater), etc. Such a formulation would set the value of each bit in exactly one place, which would make it easier to see how it should be rendered into hardware.
ISE 14.7 at synthesis returns the following warning on the subsequent line which eventually leads to an error:
"Width mismatch. <temp> has a width of 8 bits but assigned expression is 128-bit wide."
temp <= padding_start_s((((i_pad+1)*8)-1) downto (i_pad*8));
The problem seems to be with the for loop. What I am trying to do is to pad an incoming signal of N multiples of 128 bit. Eventually a non-complete 128 bit signal is received and I want to detect where it eventually ends and then add padding. Certainly, some of the code is missing, but this should really be the relevant stuff.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.NUMERIC_STD.all;
library work;
use work.keccak_globals.all;
entity Keccak_padder is
port (
clk_i : in std_logic;
data_i : in std_logic_vector(127 downto 0);
rst_n : in std_logic;
start_i : in std_logic;
end_i : in std_logic;
state_vector_o : out std_logic_vector(r-1 downto 0);
state_vector_valid_o : out std_logic;
long_message_o : out std_logic
);
end Keccak_padder;
architecture Behavioral of Keccak_padder is
signal word_count : integer range 1 to 16:=1;
signal pad_count : integer range 0 to 3:=0;
signal i_pad : integer range 0 to 15;
signal word_count : integer range 1 to 16:=1;
signal padding_start_s : std_logic_vector(127 downto 0):=(others=>'0');
signal temp : std_logic_vector(7 downto 0);
constant zero_vector : std_logic_vector(7 downto 0):=(others=>'0');
signal start_pad : std_logic;
process(clk_i, rst_n, fsm_state, pad_count, start_pad, padding_start_s)
begin
if rising_edge(clk_i) then
case fsm_state is
when IDLE =>
...
when TRANSMIT =>
...
when RECEIVE =>
if (pad_count = 1) then
state_vector_o((r-1-(data_i'length * (word_count - 1))) downto (r-(data_i'length * (word_count)))) <= temp;
pad_count <= 0;
fsm_state <= IDLE;
start_pad <= '0';
elsif (start_pad = '1') then
temp <= padding_start_s((((i_pad+1)*8)-1) downto (i_pad*8));
pad_count <= pad_count + 1;
end if;
for i in 15 downto 0 loop
if (padding_start_s((((i+1)*8)-1) downto ((i)*8)) = zero_vector) then
i_pad <= i;
start_pad <= '1';
exit;
end if;
end loop;
end case;
end if;
end process;
So eventually what I'm asking is: how do I find a way around this and why is this a problem? Is it wrong to be cutting the range in a signal assignment?
Thanks!
Without a Minimal, Complete, and Verifiable example an answer is hit or miss, and this is a synthesis issue instead of VHDL language syntax or semantic issue.
As Brian commented the temp assignment is a 16:1 mux for an 8 bit wide value, it's possible to simplify the indexing. Even more than Brian suggests:
type byte_array_16 is array (15 downto 0) of std_logic_vector (7 downto 0);
signal padding_bytes: byte_array_16;
begin
padding_bytes <= byte_array_16'(
padding_start_s(127 downto 120), padding_start_s(119 downto 112),
padding_start_s(111 downto 104), padding_start_s(103 downto 96),
padding_start_s( 95 downto 88), padding_start_s( 87 downto 80),
padding_start_s( 79 downto 72), padding_start_s( 71 downto 64),
padding_start_s( 63 downto 56), padding_start_s( 55 downto 48),
padding_start_s( 47 downto 40), padding_start_s( 39 downto 32),
padding_start_s( 31 downto 24), padding_start_s( 23 downto 16),
padding_start_s( 15 downto 8), padding_start_s( 7 downto 0)
);
TEST1: -- temp assignment expression
process
variable i_pad: integer range 0 to 15; -- overloads signal i_pad
begin
for i in 0 to 15 loop
i_pad := i;
-- temp <= padding_start_s((((i_pad + 1) * 8) - 1) downto (i_pad * 8));
temp <= padding_bytes(i_pad);
wait for 0 ns; -- temp assignment takes effect next delta cycle
end loop;
report "Test 1, temp assignment, no bounds errors";
wait;
end process;
The assignment to padding_bytes works as like a union in C, except that it's only goes one way. It also adds no hardware burden.
So the i_pad value determination is a priority encoder from a particular end with a bunch of byte recognizers comparing values to constant zero_vector. Those 16 recognizers (the for loop will get unwound in synthesis) get optimized to just look for all '0's.
What you have besides recognizers is a 16 to 4 priority encoder producing i_pad and start_pad, used to specify any recognizers found all '0's.
But what's hairy is there's all this arithmetic in what you select for inputs to the recognizers. You can fix that with the same one way union:
FIND_FIRST_ZERO_BYTE:
process
begin
start_pad <= '0';
for i in 15 downto 0 loop
if padding_bytes(i) = zero_vector then
i_pad <= i;
start_pad <= '1';
exit;
end if;
end loop;
wait;
end process;
And that eliminates a whole heck of a lot of arithmetic required because i_pad is a signal.
I have a question which is probably in 2 parts:
I am using a (nominally 32 bit) integer variable which I would like to write to an 8 bit UART as 4 bytes (i.e., as binary data)
i.e. variable Count : integer range 0 to 2147483647;
How should I chop the 32 bit integer variable into 4 separate 8 bit std_logic_vectors as expected by my UART code, and how should I pass these to the UART one byte at a time ?
I am aware std_logic_vector(to_unsigned(Count, 32)) will convert the integer variable into a 32 bit std_logic_vector, but then what ? Should I create a 32 bit std_logic_vector, assign the converted Count value to it, then subdivide it using something like the following code ? I realise the following assumes the count variable does not change during the 4 clock cycles, and assumes the UART can accept a new byte every clock cycle, and lacks any means of re-triggering the 4 byte transmit cycle, but am I on the right track here, or is there a better way ?
variable CountOut : std_logic_vector(31 downto 0);
process (clock)
variable Index : integer range 0 to 4 := 0;
begin
if rising_edge(clock) then
CountOut <= std_logic_vector(to_unsigned(Count, 32);
if (Index = 0) then
UartData(7 downto 0) <= CountOut(31 downto 24);
Index := 1;
elsif (Index = 1) then
UartData(7 downto 0) <= CountOut(23 downto 16);
Index := 2;
elsif (Index = 2) then
UartData(7 downto 0) <= CountOut(15 downto 8);
Index := 3;
elsif (Index =31) then
UartData(7 downto 0) <= CountOut(7 downto 0);
Index := 4;
else
Index := Index;
end if;
end if;
end process;
Any comments or recommendations would be appreciated.
Thanks,
MAI-AU.
You seem to be on the right track. I believe there are two basic solutions to this problem:
Register the output value as a 32-bit vector, and use different ranges for each output operation (as you did in your code example)
Register the output value as a 32-bit vector, and shift this value 8 bits at a time after each output operation. This way you can use the same range in all operations. The code below should give you an idea:
process (clock)
variable Index: integer range 0 to 4 := 0;
begin
if rising_edge(clock) then
if (Index = 0) then
CountOut <= std_logic_vector(to_unsigned(Count, 32));
Index := Index + 1;
elsif (Index < 4) then
UartData <= CountOut(31 downto 24);
CountOut <= CountOut sll 8;
Index := Index + 1;
end if;
end if;
end process;
Also, please check your assignments, in your example CountOut is declared as a variable but is assigned to as a signal.
There's nothing wrong with the code you've shown. You can do something to separate the the assignment to UartData using Index to allow a loop.
library ieee;
use ieee.std_logic_1164.all;
entity union is
end entity;
architecture foo of union is
type union32 is array (integer range 1 to 4) of std_logic_vector(7 downto 0);
signal UartData: std_logic_vector(7 downto 0);
begin
TEST:
process
variable quad: union32;
constant fourbytes: std_logic_vector(31 downto 0) := X"deadbeef";
begin
quad := union32'(fourbytes(31 downto 24), fourbytes(23 downto 16),
fourbytes(15 downto 8),fourbytes(7 downto 0));
for i in union32'RANGE loop
wait for 9.6 us;
UartData <= Quad(i);
end loop;
wait for 9.6 us; -- to display the last byte
wait; -- one ping only
end process;
end architecture;
Or use a type conversion function to hide complexity:
library ieee;
use ieee.std_logic_1164.all;
entity union is
type union32 is array (integer range 1 to 4) of std_logic_vector(7 downto 0);
end entity;
architecture fee of union is
signal UartData: std_logic_vector(7 downto 0);
function toquad (inp: std_logic_vector(31 downto 0)) return union32 is
begin
return union32'(inp(31 downto 24), inp(23 downto 16),
inp(15 downto 8), inp( 7 downto 0));
end function;
begin
TEST:
process
variable quad: union32;
constant fourbytes: std_logic_vector(31 downto 0) := X"deadbeef";
begin
quad := toquad (fourbytes);
for i in union32'RANGE loop
wait for 9.6 us;
UartData <= Quad(i);
end loop;
wait for 9.6 us; -- to display the last byte
wait; -- one ping only
end process;
end architecture;
And gives the same answer.