4bit ALU VHDL code - vhdl

I am writing a code for a 4 bit ALU and I have a problem when I want to write for shift left operation. I have two inputs (operandA and operandB ). I want to convert the operandB into decimal (for example "0010" into '2') and then shift operandA 2 times to the left. my code is compiled but I am not sure that it is true. Thank you in advance.
entity ALU is
port(
reset_n : in std_logic;
clk : in std_logic;
OperandA : in std_logic_vector(3 downto 0);
OperandB : in std_logic_vector(3 downto 0);
Operation : in std_logic_vector(2 downto 0);
Start : in std_logic;
Result_Low : out std_logic_vector(3 downto 0);
Result_High : out std_logic_vector(3 downto 0);
Ready : out std_logic;
Errorsig : out std_logic);
end ALU;
architecture behavior of ALU is
signal loop_nr : integer range 0 to 15;
begin
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
for i in 0 to loop_nr loop
loop_nr <= to_integer(unsigned(OperandB));
Result_Low <= OperandA(2 downto 0)&'0';
Result_High <= tempHigh(2 downto 0) & OperandA(3);
end loop;
Ready <= '1';
Errorsig <= '0';
when "010" =>
Result_Low <= OperandB(0)& OperandA(3 downto 1);
Result_High <= OperandB(3 downto 1);
Ready <= '1';
when others =>
Result_Low <= (others => '0');
ready <= '0';
Errorsig <= '0';
end case;
end if;
end process;
end behavior;

For shifting left twice the syntax should be the following:
A <= A sll 2; -- left shift logical 2 bits
I don't quite understand why is it required to convert operand B in decimal. It can be used as a binary or decimal value or for that matter hexadecimal value at any point of time irrelevant of the base it was saved in.

The operator sll may not always work as expected before VHDL-2008 (read more
here),
so consider instead using functions from ieee.numeric_std for shifting, like:
y <= std_logic_vector(shift_left(unsigned(OperandA), to_integer(unsigned(OperandB))));
Note also that Result_High is declared in port as std_logic_vector(3 downto
0), but is assigned in line 41 as Result_High <= OperandB(3 downto 1), with
assign having one bit less than size.
Assumption for code is that ieee.numeric_std is used.

The reason you've been urged to use the likes of sll is because in general
synthesis tools don't support loop statements with non-static bounds
(loop_nr). Loops are unfolded which requires a static value to determine how
many loop iterations are unfolded (how much hardware to generate).
As Morten points out your code doesn't analyze, contrary to you assertion
that it compiles.
After inserting the following four lines at the beginning of your code we see
an error at line 41:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--(blank, a spacer that doesn't show up in the code highlighter)
ghdl -a ALU.vhdl
ALU.vhdl:41:26: length of value does not match length of target
ghdl: compilation error
Which looks like
Result_High <= '0' & OperandB(3 downto 1);
was intended in the case statement, choice "010" (an srl equivalent hard
coded to a distance of 1, presumably to match the correct behavior of the sll
equivalent). After which your design description analyzes.
Further there are other algorithm description errors not reflected in VHDL
syntax or semantic errors.
Writing a simple test bench:
library ieee;
use ieee.std_logic_1164.all;
entity alu_tb is
end entity;
architecture foo of alu_tb is
signal reset_n: std_logic := '0';
signal clk: std_logic := '0';
signal OperandA: std_logic_vector(3 downto 0) :="1100"; -- X"C"
signal OperandB: std_logic_vector(3 downto 0) :="0010"; -- 2
signal Operation: std_logic_vector(2 downto 0):= "001"; -- shft right
signal Start: std_logic; -- Not currently used
signal Result_Low: std_logic_vector(3 downto 0);
signal Result_High: std_logic_vector(3 downto 0);
signal Ready: std_logic;
signal Errorsig: std_logic;
begin
DUT:
entity work.ALU
port map (
reset_n => reset_n,
clk => clk,
OperandA => OperandA,
OperandB => OperandB,
Operation => Operation,
Start => Start,
Result_Low => Result_Low,
Result_High => Result_High,
Ready => Ready,
Errorsig => Errorsig
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 100 ns then
wait;
end if;
end process;
STIMULUS:
process
begin
wait for 20 ns;
reset_n <= '1';
wait;
end process;
end architecture;
Gives us a demonstration:
The first thing that sticks out is that Result_High gets some 'U's. This is
caused by tempHigh not being initialized or assigned.
The next thing to notice is that the shift result is wrong (both Result_Low
and Result_High). I'd expect you'd want a "0011" in Result_High and "0000" in
Result_Low.
You see the result of exactly one left shift - ('U','U','U','1') in
Result_High and "1000" in Result_Low.
This is caused by executing a loop statement in delta cycles (no intervening
simulation time passage). In a process statement there is only one driver for
each signal. The net effect of that is that there is only one future value
for the current simulation time and the last value assigned is going to be
the one that is scheduled in the projected output waveform for the current
simulation time. (Essentially, the assignment in the loop statement to a
signal occurs once, and because successive values depend on assignment
occurring it looks like there was only one assignment).
There are two ways to fix this behavior. The first is to use variables
assigned inside the loop and assign the corresponding signals to the
variables following the loop statement. As noted before the loop bound isn't
static and you can't synthesis the loop.
The second way is to eliminate the loop by executing the shift assignments
sequentially. Essentially 1 shift per clock, signaling Ready after the last
shift occurs.
There's also away to side step the static bounds issue for loops by using a
case statement (or in VHDL 2008 using a sequential conditional signal
assignment of sequential selected signal assignment should your synthesis
tool vendor support them). This has the advantage of operating in one clock.
Note all of these require having an integer variable holding
to_integer(unsigned(OperandB)).
And all of this can be side stepped when your synthesis tool vendor supports
sll (and srl for the other case) or SHIFT_LEFT and SHIFT_RIGHT from package
numeric_std, and you are allowed to use them.
A universal (pre VHDL 2008) fix without using sll or SHIFT_LEFT might be:
begin
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable loop_int: integer range 0 to 15;
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
loop_int := to_integer(unsigned(OperandB));
case loop_int is
when 0 =>
Result_Low <= OperandA;
Result_High <= (others => '0');
when 1 =>
Result_Low <= OperandA(2 downto 0) & '0';
Result_High <= "000" & OperandA(3);
when 2 =>
Result_Low <= OperandA(1 downto 0) & "00";
Result_High <= "00" & OperandA(3 downto 2);
when 3 =>
Result_Low <= OperandA(0) & "000";
Result_High <= "0" & OperandA(3 downto 1);
when 4 =>
Result_Low <= (others => '0');
Result_High <= OperandA(3 downto 0);
when 5 =>
Result_Low <= (others => '0');
Result_High <= OperandA(2 downto 0) & '0';
when 6 =>
Result_Low <= (others => '0');
Result_High <= OperandA(1 downto 0) & "00";
when 7 =>
Result_Low <= (others => '0');
Result_High <= OperandA(0) & "000";
when others =>
Result_Low <= (others => '0');
Result_High <= (others => '0');
end case;
-- for i in 0 to loop_nr loop
-- loop_nr <= to_integer(unsigned(OperandB));
-- Result_Low <= OperandA(2 downto 0)&'0';
-- Result_High <= tempHigh(2 downto 0) & OperandA(3);
-- end loop;
Ready <= '1';
Errorsig <= '0';
Which gives:
The right answer (all without using signal loop_nr).
Note that all the choices in the case statement aren't covered by the simple
test bench.
And of course like most things there's more than two ways to get the desired
result.
You could use successive 2 to 1 multiplexers for both Result_High and
Result_Low, with each stage fed from the output of the previous stage (or
OperandA for the first stage) as the A input the select being the appropriate
'bit' from OperandB, and the B input to the multiplexers the previous stage
output shifted by 1 logically ('0' filled).
The multiplexers can be functions, components or procedure statements. By
using a three to one multiplexer you can implement both symmetrical shift
Operation specified operations (left and right). Should you want to include signed shifts,
instead of '0' filled right shifts you can fill with the sign bit value. ...
You should also be assigning Ready <= '0' for those cases where valid
successive Operation values can be dispatched.
And because your comment on one of the answers requires the use of a loop with an integer value:
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable tempLow: std_logic_vector(3 downto 0); --added
variable loop_int: integer range 0 to 15; --added
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
tempLow := OperandA; --added
tempHigh := (others => '0'); --added
loop_int := to_integer(unsigned(OperandB)); --added
-- for i in 0 to loop_nr loop
-- loop_nr <= to_integer(unsigned(OperandB));
-- Result_Low <= OperandA(2 downto 0)&'0';
-- Result_High <= tempHigh(2 downto 0) & OperandA(3);
-- end loop;
-- More added:
if loop_int /= 0 then
for i in 1 to loop_int loop
tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
-- 'read' tempLow(3) before it's updated
tempLow := tempLow(2 downto 0) & '0';
end loop;
Result_Low <= tempLow;
Result_High <= tempHigh(3 downto 0);
else
Result_Low <= OperandA;
Result_High <= (others => '0');
end if;
Ready <= '1';
Errorsig <= '0';
Which gives:
And to demonstrate both halves of Result are working OperandA's default value has been changed to "0110":
Also notice the loop starts at 1 instead of 0 to prevent you from having an extra shift and there's a check for non-zero loop_int to prevent the for loop from executing at least once.
And is it possible to make a synthesizable loop in these circumstances?
Yes.
The loop has to address all possible shifts (the range of loop_int) and test whether or not i falls under the shift threshold:
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable tempLow: std_logic_vector(3 downto 0); --added
subtype loop_range is integer range 0 to 15;
variable loop_int: integer range 0 to 15; --added
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
tempLow := OperandA; --added
tempHigh := (others => '0'); --added
loop_int := to_integer(unsigned(OperandB)); --added
for i in loop_range loop
if i < loop_int then
tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
-- 'read' tempLow(3) before it's updated
tempLow := tempLow(2 downto 0) & '0';
end if;
end loop;
Result_Low <= tempLow;
Result_High <= tempHigh(3 downto 0);

Related

VHDL Vivado Combinatorial Loop Alert

I am trying to implement a simple ALU:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity simple_alu is
Port (
clk : in std_logic;
rst : in std_logic;
op : in std_logic_vector (1 downto 0);
in0 : in std_logic_vector (31 downto 0);
in1 : in std_logic_vector (31 downto 0);
res : out std_logic_vector (31 downto 0);
done_flag : out std_logic
);
end simple_alu;
architecture Behavioral of simple_alu is
type state_type is (start, add, sub, mul, pow, done);
signal state, next_state : state_type := start;
signal status : std_logic := '0';
signal inter, shift_reg, zero : std_logic_vector (31 downto 0) := (others => '0');
begin
SYNC_PROC: process (clk, rst)
begin
if (rst = '0') then
if rising_edge(clk) then
state <= next_state;
res <= inter;
done_flag <= status;
end if;
else
state <= start;
res <= (others => '0');
done_flag <= '0';
end if;
end process;
--MEALY State-Machine - Outputs based on state and inputs
OUTPUT_DECODE: process (state, in0, in1, shift_reg)
variable result, temp : std_logic_vector (31 downto 0);
variable flag : std_logic := '0';
begin
shift_reg <= in1;
temp := temp;
flag := flag;
result := result;
case state is
when start =>
result := std_logic_vector(to_signed(1, 32));
temp := in0;
flag := '0';
when add => result := std_logic_vector(signed(in0) + signed(in1));
when sub => result := std_logic_vector(signed(in0) - signed(in1));
when mul => result := std_logic_vector(resize(signed(in0) * signed(in1), 32));
when pow =>
if (shift_reg(shift_reg'low) = '1') then
result := std_logic_vector(resize(signed(result) * signed(temp), 32));
else
result := result;
end if;
temp := std_logic_vector(resize(signed(temp) * signed(temp), 32));
shift_reg <= std_logic_vector(shift_right(signed(shift_reg), 1));
when done =>
result := result;
flag := '1';
when others =>
end case;
inter <= result;
status <= flag;
end process;
NEXT_STATE_DECODE: process (state, op, shift_reg, zero, rst) -- rst indicates that one input (op, in0 or in1) changed
begin
--declare default state for next_state to avoid latches
next_state <= state; --default is to stay in current state
case (state) is
when start =>
case (op) is
when "00" => next_state <= add;
when "01" => next_state <= sub;
when "10" => next_state <= mul;
when "11" => next_state <= pow;
when others => next_state <= done;
end case;
when add => next_state <= done;
when sub => next_state <= done;
when mul => next_state <= done;
when pow =>
if (shift_reg = zero) then
next_state <= done;
else
next_state <= pow;
end if;
when done =>
if (rst = '1') then
next_state <= start;
end if;
when others =>
end case;
end process;
end Behavioral;
This seems to be working, at least in this testbench:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity simple_alu_tb is
end simple_alu_tb;
architecture Behavioral of simple_alu_tb is
component simple_alu is
Port (
clk : in std_logic;
rst : in std_logic;
op : in std_logic_vector (1 downto 0);
in0 : in std_logic_vector (31 downto 0);
in1 : in std_logic_vector (31 downto 0);
res : out std_logic_vector (31 downto 0);
done_flag : out std_logic
);
end component;
signal clk : std_logic := '0';
signal rst : std_logic := '0';
signal op : std_logic_vector (1 downto 0) := (others => '0');
signal in0 : std_logic_vector (31 downto 0) := (others => '0');
signal in1 : std_logic_vector (31 downto 0) := (others => '0');
signal res : std_logic_vector (31 downto 0) := (others => '0');
signal done_flag : std_logic := '0';
constant clk_period : time := 1 ns;
begin
--Instantiate the Unit Under Test (UUT)
uut: simple_alu Port Map (
clk => clk,
rst => rst,
op => op,
in0 => in0,
in1 => in1,
res => res,
done_flag => done_flag
);
-- Clock process definitions
clk_process :process
begin
clk <= '1';
wait for clk_period/2;
clk <= '0';
wait for clk_period/2;
end process;
stim_proc: process
begin
wait for 2*clk_period;
rst <= '1';
wait for clk_period;
rst <= '0';
wait for 4*clk_period;
op <= "00"; -- add
in0 <= std_logic_vector(to_signed(12, 32));
in1 <= std_logic_vector(to_signed(3, 32));
rst <= '1';
wait for clk_period;
rst <= '0';
wait for 2*clk_period;
assert (res = std_logic_vector(to_signed(15, 32))) report "addition failed" severity failure;
wait for 4*clk_period;
op <= "01"; -- sub
in0 <= std_logic_vector(to_signed(12, 32));
in1 <= std_logic_vector(to_signed(3, 32));
rst <= '1';
wait for clk_period;
rst <= '0';
wait for 2*clk_period;
assert (res = std_logic_vector(to_signed(9, 32))) report "subtraction failed" severity failure;
wait for 4*clk_period;
op <= "10"; -- mul
in0 <= std_logic_vector(to_signed(12, 32));
in1 <= std_logic_vector(to_signed(3, 32));
rst <= '1';
wait for clk_period;
rst <= '0';
wait for 2*clk_period;
assert (res = std_logic_vector(to_signed(36, 32))) report "multiplication failed" severity failure;
wait for 4*clk_period;
op <= "11"; -- pow
in0 <= std_logic_vector(to_signed(12, 32));
in1 <= std_logic_vector(to_signed(7, 32));
rst <= '1';
wait for clk_period;
rst <= '0';
wait for 4*clk_period;
assert (res = std_logic_vector(to_signed(35831808, 32))) report "power failed" severity failure;
wait for 4*clk_period;
op <= "11"; -- pow
in0 <= std_logic_vector(to_signed(12, 32));
in1 <= std_logic_vector(to_signed(6, 32));
rst <= '1';
wait for clk_period;
rst <= '0';
wait for 4*clk_period;
assert (res = std_logic_vector(to_signed(2985984, 32))) report "power failed" severity failure;
wait;
end process;
end Behavioral;
I would like to implement that as AXI4-lite component. So I generate the wrapper, adapt the write process and instantiate my module as follows:
...
process (S_AXI_ACLK)
variable loc_addr :std_logic_vector(OPT_MEM_ADDR_BITS downto 0);
begin
if rising_edge(S_AXI_ACLK) then
if S_AXI_ARESETN = '0' then
-- command_reg <= (others => '0');
-- done_flag <= '0';
slv_reg1 <= (others => '0');
slv_reg2 <= (others => '0');
-- slv_reg3 <= (others => '0');
else
loc_addr := axi_awaddr(ADDR_LSB + OPT_MEM_ADDR_BITS downto ADDR_LSB);
if (slv_reg_wren = '1') then
case loc_addr is
when b"00" =>
for byte_index in 0 to (C_S_AXI_DATA_WIDTH/8-4) loop -- write to command register only
if ( S_AXI_WSTRB(byte_index) = '1' ) then
-- Respective byte enables are asserted as per write strobes
-- slave registor 0
command_reg(byte_index*8+7 downto byte_index*8) <= S_AXI_WDATA(byte_index*8+7 downto byte_index*8);
end if;
end loop;
when b"01" =>
for byte_index in 0 to (C_S_AXI_DATA_WIDTH/8-1) loop
if ( S_AXI_WSTRB(byte_index) = '1' ) then
-- Respective byte enables are asserted as per write strobes
-- slave registor 1
slv_reg1(byte_index*8+7 downto byte_index*8) <= S_AXI_WDATA(byte_index*8+7 downto byte_index*8);
end if;
end loop;
when b"10" =>
for byte_index in 0 to (C_S_AXI_DATA_WIDTH/8-1) loop
if ( S_AXI_WSTRB(byte_index) = '1' ) then
-- Respective byte enables are asserted as per write strobes
-- slave registor 2
slv_reg2(byte_index*8+7 downto byte_index*8) <= S_AXI_WDATA(byte_index*8+7 downto byte_index*8);
end if;
end loop;
-- when b"11" => -- do not write to reg3
-- for byte_index in 0 to (C_S_AXI_DATA_WIDTH/8-1) loop
-- if ( S_AXI_WSTRB(byte_index) = '1' ) then
-- -- Respective byte enables are asserted as per write strobes
-- -- slave registor 3
-- slv_reg3(byte_index*8+7 downto byte_index*8) <= S_AXI_WDATA(byte_index*8+7 downto byte_index*8);
-- end if;
-- end loop;
when others =>
command_reg <= command_reg;
done_flag <= done_flag;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
-- slv_reg3 <= slv_reg3;
end case;
end if;
end if;
end if;
end process;
...
-- Add user logic here
-- byte0 byte1 byte2 byte3
slv_reg0 <= done_flag & "0000000" & "00000000" & "00000000" & command_reg;
alu : simple_alu
port map (
clk => S_AXI_ACLK,
rst => slv_reg_wren, -- reset on every write to a register, high active
op => command_reg(1 downto 0),
in0 => slv_reg1,
in1 => slv_reg2,
res => slv_reg3,
done_flag => done_flag
);
-- User logic ends
But when I try to generate the bitstream for my wrapper design which includes the Zync UltraScale+ MPSoC, AXI Interconnect, Processor System Reset and my AXI Peripheral I get the following error:
ERROR: [DRC LUTLP-1] Combinatorial Loop Alert: 1 LUT cells form a combinatorial loop. This can create a race condition. Timing analysis may not be accurate. The preferred resolution is to modify the design to remove combinatorial logic loops. If the loop is known and understood, this DRC can be bypassed by acknowledging the condition and setting the following XDC constraint on any one of the nets in the loop: 'set_property ALLOW_COMBINATORIAL_LOOPS TRUE [get_nets <myHier/myNet>]'. One net in the loop is design_1_i/simple_alu_0/U0/simple_alu_v1_0_S00_AXI_inst/alu/state[0]_i_2_n_0. Please evaluate your design. The cells in the loop are: design_1_i/simple_alu_0/U0/simple_alu_v1_0_S00_AXI_inst/alu/state[0]_i_2.
ERROR: [DRC LUTLP-1] Combinatorial Loop Alert: 1 LUT cells form a combinatorial loop. This can create a race condition. Timing analysis may not be accurate. The preferred resolution is to modify the design to remove combinatorial logic loops. If the loop is known and understood, this DRC can be bypassed by acknowledging the condition and setting the following XDC constraint on any one of the nets in the loop: 'set_property ALLOW_COMBINATORIAL_LOOPS TRUE [get_nets <myHier/myNet>]'. One net in the loop is design_1_i/simple_alu_0/U0/simple_alu_v1_0_S00_AXI_inst/alu/state[1]_i_3_n_0. Please evaluate your design. The cells in the loop are: design_1_i/simple_alu_0/U0/simple_alu_v1_0_S00_AXI_inst/alu/state[1]_i_3.
Please excuse the huge amount of code, I couldn't find a way to show the error with a smaller example.
I tried the solution proposed here:
set_property SEVERITY {Warning} [get_drc_checks LUTLP-1]
But that did nothing. I also tried setting set_property ALLOW_COMBINATORIAL_LOOPS TRUE for the two nets but that leaves me unsure about the functionality of my circuit. I am using Vivado v2018.3, my target is the Ultra96 from Avnet. Any clues?
EDIT: I have updated the code to reflect the current implementation, I get warnings about latches for result_reg, flag_reg and temp_reg. How do I resolve those?
After a long struggle I finally came up with this solution:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity simple_alu is
Port (
clk : in std_logic;
rst : in std_logic;
op : in std_logic_vector (1 downto 0);
in0 : in std_logic_vector (31 downto 0);
in1 : in std_logic_vector (31 downto 0);
res : out std_logic_vector (31 downto 0);
done_flag : out std_logic
);
end simple_alu;
architecture Behavioral of simple_alu is
type state_type is (start, add, sub, mul, pow, done);
signal state, next_state : state_type := start;
signal result, next_result, temp, next_temp, shift_reg, next_shift_reg, zero : std_logic_vector (31 downto 0) := (others => '0');
signal next_done_flag : std_logic := '0';
begin
SYNC_PROC: process (clk, rst)
begin
if rising_edge(clk) then
if (rst = '1') then
state <= start;
else
state <= next_state;
res <= next_result;
result <= next_result;
temp <= next_temp;
shift_reg <= next_shift_reg;
done_flag <= next_done_flag;
end if;
end if;
end process;
--MEALY State-Machine - Outputs based on state and inputs
OUTPUT_DECODE: process (state, result, in0, in1, temp, shift_reg)
begin
next_done_flag <= '0';
next_result <= result;
next_shift_reg <= shift_reg;
next_temp <= temp;
case state is
when start =>
next_result <= std_logic_vector(to_signed(1, 32));
next_temp <= in0;
next_shift_reg <= in1;
when add => next_result <= std_logic_vector(signed(in0) + signed(in1));
when sub => next_result <= std_logic_vector(signed(in0) - signed(in1));
when mul => next_result <= std_logic_vector(resize(signed(in0) * signed(in1), 32));
when pow =>
if (shift_reg(shift_reg'low) = '1') then
next_result <= std_logic_vector(resize(signed(result) * signed(temp), 32));
else
next_result <= result;
end if;
next_temp <= std_logic_vector(resize(signed(temp) * signed(temp), 32));
next_shift_reg <= std_logic_vector(shift_right(signed(shift_reg), 1));
when done => next_done_flag <= '1';
when others =>
end case;
end process;
NEXT_STATE_DECODE: process (state, op, shift_reg, zero)
begin
--declare default state for next_state to avoid latches
next_state <= state; --default is to stay in current state
case (state) is
when start =>
case (op) is
when "00" => next_state <= add;
when "01" => next_state <= sub;
when "10" => next_state <= mul;
when "11" => next_state <= pow;
when others => next_state <= done;
end case;
when add => next_state <= done;
when sub => next_state <= done;
when mul => next_state <= done;
when pow =>
if (shift_reg = zero) then
next_state <= done;
else
next_state <= pow;
end if;
when done =>
when others =>
end case;
end process;
end Behavioral;
The problem was that I did not understand how hardware description works, now I know a little (at least I hope so..). Especially how clocked and unclocked processes are connected (save intermediate results in registers). I will leave this question up just in case another beginner stumbles upon the same issue. If you think I should remove it, please state that in a comment and I will do so.
Here are some resources that helped me:
this question and in particular the accepted answer
some rules I picked up somewhere:
Don't read from the signals to which you write.
Have a correct sensitivity list (all signals that you read should be in the sensitivity list)
Make sure that all signals to which your write are assigned in every path. (for example: in each branch of an if-else-statement)
For processes which use variable, make sure every variable is initialized a default value before reading it (in another variable or signal ).

VHDL Parametric Division Circuit - Book: FPGA Prototyping by VHDL Examples, Pong Chu

I'm trying to follow an example on my VHDL book. Its name is FPGA Prototyping by VHDL Examples, Pong Chu. It has a Divider Circuit example in Chapter 6, Listing 5. I understood the general idea of a division operation. To verify the module I wrote a testbench and I saw that it doesn't work properly. If anyone could explain to me where the problem is, I would be very appreciated.
Here are the codes of module and testbench.
Module:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity Divider is
generic(W: integer := 8;
CBIT: integer := 4 );
Port ( clk, reset : in STD_LOGIC;
start : in STD_LOGIC;
dvsr, dvnd : in STD_LOGIC_VECTOR (W-1 downto 0);
ready, done_tick : out STD_LOGIC;
quo, rmd : out STD_LOGIC_VECTOR (W-1 downto 0));
end Divider;
architecture Behavioral of Divider is
type state_type is (idle, op, last, done);
signal state_reg, state_next: state_type;
signal rh_reg, rh_next: UNSIGNED(W-1 downto 0) := (others => '0');
signal rl_reg, rl_next: STD_LOGIC_VECTOR(W-1 downto 0) := (others => '0');
signal rh_temp: UNSIGNED(W-1 downto 0) := (others => '0');
signal d_reg, d_next: UNSIGNED(W-1 downto 0) := (others => '0');
signal n_reg, n_next: UNSIGNED(CBIT-1 downto 0) := (others => '0');
signal q_bit: STD_LOGIC;
begin
-- FSMD State and Data Registers
process(clk, reset)
begin
if reset = '1' then
state_reg <= idle;
rh_reg <= (others => '0');
rl_reg <= (others => '0');
d_reg <= (others => '0');
n_reg <= (others => '0');
elsif rising_edge(clk) then
state_reg <= state_next;
rh_reg <= rh_next;
rl_reg <= rl_next;
d_reg <= d_next;
n_reg <= n_next;
end if;
end process;
-- FSMD Next-State Logic and Data Path Logic
process(state_reg, n_reg, rh_reg, rl_reg, d_reg, start, dvsr, dvnd, q_bit, rh_temp, n_next)
begin
ready <= '0';
done_tick <= '0';
state_next <= state_reg;
rh_next <= rh_reg;
rl_next <= rl_reg;
d_next <= d_reg;
n_next <= n_reg;
case state_reg is
when idle =>
ready <= '1';
if start = '1' then
rh_next <= (others => '0');
rl_next <= dvnd; -- Dividend
d_next <= UNSIGNED(dvsr); -- Divisor
n_next <= TO_UNSIGNED(W+1, CBIT); -- Index
state_next <= op;
end if;
when op =>
--Shift rh and rl left
rl_next <= rl_reg(W-2 downto 0) & q_bit;
rh_next <= rh_temp(W-2 downto 0) & rl_reg(W-1);
--Decrease index
n_next <= n_reg - 1;
if(n_next = 1) then
state_next <= last;
end if;
when last =>
rl_next <= rl_reg(W-2 downto 0) & q_bit;
rh_next <= rh_temp;
state_next <= done;
when done =>
state_next <= idle;
done_tick <= '1';
end case;
end process;
-- Compare and Subtract
process(rh_reg, d_reg)
begin
if rh_reg <= d_reg then
rh_temp <= rh_Reg - d_reg;
q_bit <= '1';
else
rh_temp <= rh_reg;
q_bit <= '0';
end if;
end process;
-- Output
quo <= rl_reg;
rmd <= STD_LOGIC_VECTOR(rh_reg);
end Behavioral;
Testbench:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity tb_Divider is
-- Port ( );
end tb_Divider;
architecture Behavioral of tb_Divider is
signal clk, reset, start, ready, done: STD_LOGIC;
signal dvsr, dvnd: STD_LOGIC_VECTOR(7 downto 0);
signal quo, rmd: STD_LOGIC_VECTOR(7 downto 0);
component Divider is
port( clk, reset : in STD_LOGIC;
start : in STD_LOGIC;
dvsr, dvnd : in STD_LOGIC_VECTOR (7 downto 0);
ready, done_tick : out STD_LOGIC;
quo, rmd : out STD_LOGIC_VECTOR (7 downto 0));
end component Divider;
begin
UUT: Divider port map( clk => clk, reset => reset, start => start, dvsr => dvsr, dvnd => dvnd,
ready => ready, done_tick => done, quo => quo, rmd => rmd);
process
begin
clk <= '0';
wait for 10 ns;
clk <= '1';
wait for 10 ns;
end process;
process
begin
start <= '0';
dvnd <= x"00";
dvsr <= x"00";
wait for 100 ns;
start <= '1';
dvnd <= x"C8";
dvsr <= x"0A";
wait for 10 us;
end process;
end Behavioral;
Result of Testbench:

Array aggregation on self-defined types?

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.

ALU implementation w/ ADDER

Hello I'm trying to create a 32-bit adder with a few opcodes and I've got it working quite well except for two cases and I can't seem to find what's causing them.. Maybe you can help me?
The subtraction case always fails for some reason and the ADDC case fails to compute the correct output when it should be using the carry bit in c_reg that has been created by the ADDS operation.
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY ALU IS
GENERIC(WIDTH : NATURAL := 32);
PORT(Clk : IN STD_LOGIC := '0';
Reset : IN STD_LOGIC := '0';
A : IN STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
B : IN STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
Op : IN STD_LOGIC_VECTOR(3 DOWNTO 0) := (OTHERS => '0');
Outs : OUT STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0));
END ALU;
ARCHITECTURE arch_ALU OF ALU IS
COMPONENT adder
PORT(OpA : IN STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0);
OpB : IN STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0);
Cin : IN STD_LOGIC;
Cout : OUT STD_LOGIC;
Result : OUT STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0));
END COMPONENT;
SIGNAL adder_output : STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL B_neg : STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
SIGNAL c_flag : STD_LOGIC := '0';
SIGNAL c_reg : STD_LOGIC := '0';
SIGNAL cin : STD_LOGIC := '0';
BEGIN
adder_comp : adder
PORT MAP(OpA => A,
OpB => B_neg,
Cin => cin,
Result => adder_output,
Cout => c_flag);
WITH Op SELECT
B_neg <= NOT(B) WHEN "1000",
B WHEN OTHERS;
WITH Op SELECT
cin <= '1' WHEN "1000", -- SUB
c_reg WHEN "0111", -- ADDC
'0' WHEN OTHERS; -- ADD/ADDS
ALU_Process:
PROCESS(Clk)
BEGIN
IF Reset = '0' THEN
Outs <= (OTHERS => '0');
ELSIF rising_edge(Clk) THEN
CASE Op IS
WHEN "0001" => Outs <= A AND B;
WHEN "0010" => Outs <= A OR B;
WHEN "0011" => Outs <= A NOR B;
WHEN "0100" => Outs <= A XOR B;
WHEN "0101" => Outs <= adder_output; -- ADD
WHEN "0110" => Outs <= adder_output; -- ADDS
c_reg <= c_flag;
WHEN "0111" => Outs <= adder_output; -- ADDC
WHEN "1000" => Outs <= adder_output; -- SUB
WHEN "1001" => Outs <= STD_LOGIC_VECTOR(UNSIGNED(A) SLL to_integer(UNSIGNED(B(4 DOWNTO 0))));
WHEN "1010" => Outs <= STD_LOGIC_VECTOR(unsigned(A) SRL to_integer(UNSIGNED(B(4 DOWNTO 0))));
WHEN "1011" => Outs <= STD_LOGIC_VECTOR(shift_right(SIGNED(A),to_integer(UNSIGNED(B(4 DOWNTO 0)))));
WHEN OTHERS => Outs <= (OTHERS => '0');
END CASE;
END IF;
END PROCESS;
END arch_ALU;
Only the ADDS operation should write it's carry-out to the c_reg and ADDC operation should take the c_reg into account when calculating it's output
The adder is tested and working correctly so the problem is not in the adder design.
First of all I'd like to get to know the problem of the subtraction because it is doing subtraction but the result is a little bit off.. For example:
A : h'E6A4960F
B : h'7B494E34
Op : d'1000
Outs: h'6B5B47DA while it should be h'6B5B47DB
A : h'EFDE31A3
B : h'0BCAB8FA
Op : d'1000
Out : h'E41378BB while should be h'E41378A9
Can you spot my misstake? Cause I certainly can't..
While you didn't provide a Minimal, Complete, and Verifiable example the reader can at least test the subtract portions of your design that are present:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity alu is
generic (width: natural := 32);
port (
clk: in std_logic := '0';
reset: in std_logic := '0';
a: in std_logic_vector(width - 1 downto 0) := (others => '0');
b: in std_logic_vector(width - 1 downto 0) := (others => '0');
op: in std_logic_vector(3 downto 0) := (others => '0');
outs: out std_logic_vector(width - 1 downto 0)
);
end alu;
architecture arch_alu of alu is
-- component adder
-- port (
-- opa: in std_logic_vector(width - 1 downto 0);
-- opb: in std_logic_vector(width - 1 downto 0);
-- cin: in std_logic;
-- cout: out std_logic;
-- result: out std_logic_vector(width - 1 downto 0)
-- );
-- end component;
procedure adder (
signal opa: in std_logic_vector(width - 1 downto 0);
signal opb: in std_logic_vector(width - 1 downto 0);
signal cin: in std_logic;
signal cout: out std_logic;
signal result: out std_logic_vector(width - 1 downto 0)
) is
variable sum: unsigned (width downto 0);
begin
sum := unsigned('0' & opa) + unsigned(opb) + unsigned'(""& cin);
result <= std_logic_vector(sum (width - 1 downto 0));
cout <= sum(width);
end procedure;
signal adder_output: std_logic_vector(width - 1 downto 0) := (others => '0');
signal b_neg: std_logic_vector(width - 1 downto 0) := (others => '0');
signal c_flag: std_logic := '0';
signal c_reg: std_logic := '0';
signal cin: std_logic := '0';
begin
adder_comp:
adder
-- port map (
(
opa => a,
opb => b_neg,
cin => cin,
result => adder_output,
cout => c_flag
);
with op select
b_neg <= not b when "1000",
b when others;
with op select
cin <= '1' when "1000", -- sub
c_reg when "0111", -- addc
'0' when others; -- add/adds
alu_process:
process(clk)
begin
if reset = '0' then
outs <= (others => '0');
elsif rising_edge(clk) then
case op is
when "0001" => outs <= a and b;
when "0010" => outs <= a or b;
when "0011" => outs <= a nor b;
when "0100" => outs <= a xor b;
when "0101" => outs <= adder_output; -- add
when "0110" => outs <= adder_output; -- adds
c_reg <= c_flag;
when "0111" => outs <= adder_output; -- addc
when "1000" => outs <= adder_output; -- sub
when "1001" => outs <= std_logic_vector (
unsigned(a) sll to_integer(unsigned(b(4 downto 0)))
);
when "1010" => outs <= std_logic_vector (
unsigned(a) srl to_integer(unsigned(b(4 downto 0)))
);
when "1011" => outs <= std_logic_vector (
shift_right(signed(a),to_integer(unsigned(b(4 downto 0))))
);
when others => outs <= (others => '0');
end case;
end if;
end process;
end arch_alu;
library ieee;
use ieee.std_logic_1164.all;
entity alu_tb is
end entity;
architecture foo of alu_tb is
constant width: integer := 32;
signal clk: std_logic := '0';
signal reset: std_logic := '0';
signal a: std_logic_vector(width - 1 downto 0) := (others => '0');
signal b: std_logic_vector(width - 1 downto 0) := (others => '0');
signal op: std_logic_vector(3 downto 0) := (others => '0');
signal outs: std_logic_vector(width - 1 downto 0);
begin
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 90 ns then
wait;
end if;
end process;
DUT:
entity work.alu
port map (
clk => clk,
reset => reset,
a => a,
b => b,
op => op,
outs => outs
);
STIMULUS:
process
begin
wait for 20 ns;
reset <= '1';
a <= x"E6A4960F";
b <= x"7B494E34";
op <= "1000";
wait for 20 ns;
a <= x"EFDE31A3";
b <= x"0BCAB8FA";
wait for 20 ns;
wait;
end process;
end architecture;
I wrote a quick and dirty procedure adder. This eliminates your adder entity/architecture and the component declaration.
I added a simple testbench for the two subtractions, this eliminates your test bench or testing procedure.
And this gives:
And as you can see the results are what you claim as correct.
So what that leaves is either your adder or your testbench (it's a stretch to blame your component declaration).
So what we get from this is that you haven't presented enough information to determine where the error is.
I did this little demonstration because the two errors don't have all the wrong bits in common. If you've tested your adder and are sure of it, it's probably the stimulus input when subtracting.

How to send some data 10 times with a delay of 10 ms between chunks of databits to the TX port of uart

I have an sensor it has an unlocked byte sequence which needs to be sent to it to unlock it and then it can receive the other command data.
The sensor receive data at a baudrate of 115200 bps, 8 data bits, even parity, 2 stop bits.
and before receiving any command data( used to set parameters) It needs to recieve d4 (hexadecimal number, byte) 10 times at an interval of 1ms.
I send the d4 converted into bits 11010100 added with parity and stop bits becomes 11010100011 to the TX port of uart at the baud rate of 115200 but how to create a delay between two d4 data byte sent ? I am writing the code if not clear please let me know I would put more details.
entity Uart_tx is
port (
TX : out std_logic;
clk_in : in std_logic;
but_div_clk : out std_logic;
clk_in_2 : in std_logic
);
end Uart_tx;
architecture Behavioral of Uart_tx is
signal tx_clk : std_logic := '0';
signal clk_1Khz : std_logic := '0';
signal q : unsigned(8 downto 0) := (others => '0');
signal p : unsigned(8 downto 0) := (others => '0');
type state_type is (idle, start);
signal state : state_type;
signal tick_in : std_logic := '0';
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 curr_byte : std_logic_vector(7 downto 0);
signal byte_index : unsigned(2 downto 0) := "000";
subtype reg is std_logic_Vector(10 downto 0);
type reg_array is array(natural range <>) of reg;
signal TxDataReg_array : reg_array(1 to 8);
signal cur_Tx_reg : std_logic_vector(10 downto 0);
signal current_reg : unsigned(3 downto 0) := "0001";
signal count : unsigned (4 downto 0) := (others => '0');
signal count_d : unsigned (4 downto 0) := (others => '0');
signal sent_d4 : unsigned (3 downto 0) := (others => '0');
signal send_d4 : std_logic := '1';
signal D_4 : std_logic_vector(10 downto 0) :="11000101011";
begin
-- below are random entry ..actual data will come from slv_reg registers.
data_byte_array(1) <= "10101010"; -- slv_reg0(7 downto 0);
data_byte_array(2) <= "10101011"; -- slv_reg0(15 downto 8);
data_byte_array(3) <= "10101010"; -- slv_reg0(23 downto 16);
data_byte_array(4) <= "10101011"; -- slv_reg0(31 downto 24);
data_byte_array(5) <= "10101010"; -- slv_reg1(39 downto 32);
data_byte_array(6) <= "10101011"; -- slv_reg1(47 downto 40);
data_byte_array(7) <= "10101010"; -- slv_reg1(55 downto 48);
data_byte_array(8) <= "10101011"; -- slv_reg1(63 downto 56);
tick_in <= '1';
---------------------------------------Clk_div-----------------------------------------
process ( clk_in ) is
begin
if clk_in'event and clk_in = '1' then
q <= q + 1;
tx_clk <= q(8); --- 58.gdfg/2^8 =~ 230Khz baud rate = 115200
but_div_clk <= tx_clk;
end if;
end process;
---------------------------------------Clk_div------------------------------------------
---------------------------------------Clk_div------------------------------------------
process( clk_in_2 ) is
begin
if clk_in_2'event and clk_in_2 = '1' then
p <= p + 1;
clk_1Khz <= p(7);
end if;
end process;
---------------------------------------------------------------------------------------
--------------------------------------TX_Process----------------------------------------
process( state, tx_clk , tick_in) is
variable parity : std_logic := '0';
variable curr_byte : std_logic_vector(7 downto 0) := (others => '0');
begin
case state is
when idle => TX <= '1';
if tick_in = '1' then
state <= start;
else
TX <= '1';
end if;
when start =>
if send_d4 = '1' then
if (rising_edge(clk_1Khz)) then
case count_d is
when "00000" => TX <= D_4(0);
when "00001" => TX <= D_4(1);
when "00010" => TX <= D_4(2);
when "00011" => TX <= D_4(3);
when "00100" => TX <= D_4(4);
when "00101" => TX <= D_4(5);
when "00110" => TX <= D_4(6);
when "00111" => TX <= D_4(7);
when "01000" => TX <= D_4(8);
when "01001" => TX <= D_4(9);
when "01010" => TX <= D_4(10);
when others => TX <= '1';
end case;
count_d <= count_d +1;
sent_d4 <= sent_d4 + 1;
if to_integer(count_d) = 11 then
count_d <= "00000";
end if;
if to_integer(sent_d4) = 10 then
send_d4 <= '0' ;
end if;
end if;
else
for i in 1 to 8 loop
curr_byte := data_byte_array(i);
parity := '0';
for j in curr_byte'range loop
parity := parity xor curr_byte(j);
end loop;
if parity = '0' then
TxDataReg_array(i) <= "110" & curr_byte ;
else
TxDataReg_array(i) <= "111" & curr_byte ;
end if;
end loop;
cur_Tx_reg <= TxDataReg_array(to_integer(byte_index)+1);
byte_index <= byte_index + 1;
if rising_edge(tx_clk) then
case count is
when "00000" => TX <= cur_Tx_reg(0);
when "00001" => TX <= cur_Tx_reg(1);
when "00010" => TX <= cur_Tx_reg(2);
when "00011" => TX <= cur_Tx_reg(3);
when "00100" => TX <= cur_Tx_reg(4);
when "00101" => TX <= cur_Tx_reg(5);
when "00110" => TX <= cur_Tx_reg(6);
when "00111" => TX <= cur_Tx_reg(7);
when "01000" => TX <= cur_Tx_reg(8);
when "01001" => TX <= cur_Tx_reg(9);
when "01010" => TX <= cur_Tx_reg(10);
when others => TX <= '1';
end case;
count <= count+1;
if to_integer(count) = 11 then
count <= "00000";
state <= idle;
-- TX <= '1';
end if;
end if;
end if;
when others => TX <= '1';
end case;
end process;
end Behavioral;
To get a timed delay you have to implement a counter that ticks off a computed number of clock cycles equal to 1 ms. You then need to insert states into your FSM that activate the counter and wait for it to complete when needed. It is possible to manually calculate the counter value but you can make the tools do the work for you and avoid having magic numbers in your code.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
...
constant CLOCK_FREQ : real := 50.0e6; -- 50 MHz system clock
constant SENSOR_DELAY : real := 1.0e-3; -- 1 ms delay
constant DELAY_COUNT : natural := integer(CLOCK_FREQ * SENSOR_DELAY);
-- This could be auto calculated with a ceil_log2() function
constant TIMER_SIZE : natural := 16;
signal timer : unsigned(TIMER_SIZE-1 downto 0);
constant DELAY_INIT : unsigned(timer'range)
:= to_unsigned(DELAY_COUNT, timer'length);
...
-- Initialize the timer sometime before you want the delay
timer <= DELAY_INIT;
...
-- Somewhere in your FSM
when WAIT_1MS =>
timer <= timer - 1;
if timer = 0 then
state <= WHATEVER_YOU_WANT_NEXT;
end if;
This method of using real constants to compute integer values is subject to rounding errors and general floating-point inaccuracies. For these sort of long delays, the small error (typically off-by-one) that could happen isn't usually of concern.
Note that you need to rework your state machine to follow more conventional patterns. You have created a process that mixes pure combinational logic with synchronous. You should not mix the two. You should not have the rising_edge() tests inside your FSM case statement but rather there should be a single if-block evaluating rising_edge() that contains your FSM.
If you need a clock sensitive process then it should only have the clock and an (optional) asynchronous reset in its sensitivity list. Any other pure combinational code should be put in a separate process. In a design such as this that shouldn't be necessary though.
VHDL allows what you have now but synthesis tools expect the use of a more limited style when describing the hardware. You risk having unexpected results if they can handle your code at all.

Resources