EmbDev.net

Forum: FPGA, VHDL & Verilog Reusing registers in VHDL FSM code


von Darian Reyes (Guest)


Attached files:

Rate this post
useful
not useful
Hello,

I need to write a Finite State Machine (FSM) in VHDL code and  want to 
have several computations being processed at the same time (a standard 
pipeline). In every state I have several operations to be calculated and 
I employ registers for the result of each one. I strongly need to reuse 
these registers, for example: Register 1 is filled in State 1 (as a 
result of a multiplication) and it is used in the State 2 and State 3 
(as parameter of other operations), then in the State 4, I want to save 
a new operation result (another multiplication) in Register 1 reusing 
it.

My code works in Simulation in Xilinx Vivado 2019, but when I implement 
the desing in a real FPGA (Basys 3 Artix-7) it doesn't work. I realized 
that the problem is that the correct values are not saved when I reuse 
the registers. Sometimes, the first time I reuse them, they keep the 
correct value, but already in the second reuse in later FSM states, the 
stored values are not correct, I mean, they do not correspond to the 
result of the operation that I am trying to save in the register.

Next, an example of my FSM design:
1
LIBRARY IEEE;
2
USE IEEE.std_logic_1164.all;
3
USE IEEE.numeric_std.ALL;
4
5
ENTITY test1_arith IS
6
GENERIC (
7
    ap_bit_width : positive := 4;
8
    ap_latency : positive := 2
9
);
10
PORT (
11
    I1 : IN STD_LOGIC_VECTOR(ap_bit_width - 1 downto 0);
12
    I2 : IN STD_LOGIC_VECTOR(ap_bit_width - 1 downto 0);
13
    I3 : IN STD_LOGIC_VECTOR(ap_bit_width - 1 downto 0);
14
    O1 : OUT STD_LOGIC_VECTOR(ap_bit_width - 1 downto 0);
15
    ap_clk : IN STD_LOGIC;
16
    ap_rst : IN STD_LOGIC;
17
    ap_start : IN STD_LOGIC;
18
    ap_done : OUT STD_LOGIC;
19
    ap_idle : OUT STD_LOGIC;
20
    ap_ready : OUT STD_LOGIC
21
);
22
END;
23
24
ARCHITECTURE test1_arith_arch OF test1_arith IS
25
    ATTRIBUTE CORE_GENERATION_INFO : STRING;
26
    ATTRIBUTE CORE_GENERATION_INFO OF test1_arith_arch : ARCHITECTURE IS "Test,VHDLbyMOEA,{HLS_SYN_LAT=2}";
27
    CONSTANT ap_const_logic_1 : STD_LOGIC := '1';
28
    CONSTANT ap_const_logic_0 : STD_LOGIC := '0';
29
    TYPE state IS (state_1,state_2,state_3);
30
    SIGNAL state_present: state;
31
    SIGNAL state_future: state; 
32
    SIGNAL Flag: Integer:=0;
33
     --Signal RF : STD_LOGIC_VECTOR_array;
34
    FUNCTION ALU ( Op: IN integer range 0 TO 23;
35
     A, B: IN STD_LOGIC_VECTOR (ap_bit_width - 1 downto 0) )
36
    RETURN std_logic_vector is variable Result : std_logic_vector(ap_bit_width - 1 downto 0);        
37
38
    variable A_int: Integer:=0;
39
    variable B_int: Integer:=0;
40
    variable Result_int: Integer:=0;
41
    begin
42
    A_int := to_integer(unsigned(A));
43
    B_int := to_integer(unsigned(B));
44
    With Op Select Result_int:=
45
        to_integer(unsigned(NOT A)) When 0,
46
        to_integer(unsigned(A AND B)) When 1,
47
        to_integer(unsigned(A OR B)) When 2,
48
        to_integer(unsigned(A NAND B)) When 3,
49
        to_integer(unsigned(A NOR B)) When 4,
50
        to_integer(unsigned(A XOR B)) When 5,
51
        to_integer(unsigned(A XNOR B)) When 6,
52
        (A_int + B_int) When 7,
53
        (A_int - B_int) When 8,
54
        (A_int * B_int) When 9,
55
        (A_int / B_int) When 10,
56
        ABS(A_int) When 11,
57
        (A_int ** B_int) When 12,
58
        (A_int MOD B_int) When 13,
59
        to_integer(unsigned(A) & unsigned(B)) When 14,
60
        to_integer(unsigned(A) SLL B_int) When 15,
61
        to_integer(unsigned(A) SRL B_int) When 16,
62
        to_integer(unsigned(A) SLA B_int) When 17,
63
        to_integer(unsigned(A) SRA B_int) When 18,
64
        to_integer(unsigned(A) ROL B_int) When 19,
65
        to_integer(unsigned(A) ROR B_int) When 20,
66
        to_integer(unsigned(A) & unsigned(B)) When 21,
67
        to_integer(unsigned(A) & unsigned(B)) When 22,
68
                 0   When others;
69
    return STD_LOGIC_VECTOR (TO_UNSIGNED (Result_int, (ap_bit_width)));
70
    END FUNCTION;
71
72
    SHARED VARIABLE R1:std_logic_vector(ap_bit_width - 1 downto 0);    
73
74
75
    BEGIN
76
77
    OP_FSM : PROCESS (state_present)
78
79
     BEGIN 
80
    CASE state_present IS
81
82
    WHEN state_1=> 
83
    R1 := ALU(Op => 7 ,A => I1,B => I2);
84
    Flag<=1;
85
    IF (Flag=1) THEN
86
    state_future <= state_2;
87
    END IF;
88
89
    WHEN state_2=> 
90
    R1:= ALU(Op => 7 ,A => R1, B => I3);
91
    Flag<=2;
92
    IF (Flag=2) THEN
93
    state_future <= state_3;
94
    END IF;
95
96
    WHEN state_3=> 
97
    O1<= ALU(Op => 7 ,A => R1,B => "0001");
98
    Flag<=3;
99
    IF (Flag=3) THEN
100
    state_future <= state_1;
101
    END IF;
102
    END CASE;
103
    END PROCESS OP_FSM;
104
105
    CLK_FSM : PROCESS (ap_clk)
106
    BEGIN
107
    IF (ap_clk = '1' AND ap_clk'EVENT) THEN
108
    state_present <= state_future;
109
    END IF;
110
    END PROCESS CLK_FSM;
111
112
END test1_arith_arch;

In this case, I want to reuse R1 and it works well in Simulation with 
Xilinx Vivado (1 + 4 + 0 + 1 = 6):

Figure 1.

Unfortunately, in the Basys 3 FPGA Artix-7 I don't get the correct 
results:

Figure 2.

In this figure, I show the Case 10 in a FPGA, it should get 6 (1 + 4 + 0 
+ 1) as result, but it gets 14 instead:

Figure 3.

In the tests that I have been doing I realized that it works better when 
before assigning a new value in the registry the value of the record is 
made zero before reassigning a value, for example:
1
WHEN state_3=> 
2
    R4<="0000"
3
    IF( R4 = "0000") then
4
    R4<= ALU(Op => 7 ,A=> R2,B=> R3, C =>"0000");
5
    Flag <=3;
6
    IF (Flag =3) THEN
7
    state_future <= state_4;
8
END IF;
9
END IF;

Using this form I can reuse a register once, the second time I want to 
reassign a value to the register, incorrect values are shown in the 
output.

I declarated the registers as SHARED VARIABLE and SIGNALS and I have the 
same problem with both.

I appreciate any suggestion or idea, thanks a lot.

von Lothar M. (Company: Titel) (lkmiller) (Moderator)


Rate this post
useful
not useful
Darian Reyes wrote:
> OP_FSM : PROCESS (state_present)
This sensitiviy list is incomplete! I2, I3, R1 and this strange "Flag" 
is missing. Therefore the simulation is WRONG! The toolchain tells you 
that with a "Info" or a "Warning"...

What the heck is that thing with this "Flag"? Where did you see this 
kind of coding?

One word about "variables" espacially "shared variables": you don't need 
both of them at all.

von Vancouver (Guest)


Rate this post
useful
not useful
You have implemented the ALU logic completely as a large combinatorial 
function. This will result in an incredible large and incredible slow 
unstructured sea of connected lookup tables.
For example: One of the alu function is a division. Make yourself 
familar with division algorithms for hardware and then try to imagine 
how they would behave if implemented without any pipelining or 
sequential elements.

Ok, let us assume for the moment, that your alu is functionally 
correct. Anyway, it will be slooooow. On the other hand, your state 
machine does a state transition in every clock cycle. So how fast is 
your clock? Lets say, 20MHz? That is, your alu has 50ns of time for 
computation. This is fine for AND and XOR, but surely not for 
unpipelined MULT and DIV.

Did you set any timing constraints in your design? Does Vivado know 
about which frequency your design is intended to run at? If yes, does 
the timing report complain about any timing violations?

When looking at your code, I don't see a VHDL design. I see a piece of 
software that somebody has translated into VHDL statements. There is a 
good reason why the simulation is correct, and the hardware fails: It is 
a simulation model, not a hardware design. There is almost no chance for 
the synthesis tools to translate this into an efficient hardware.

Before starting with VHDL, learn about digital design, how to implement 
arithmetics if you only have gates and flipflops. Expect that division 
in hardware is much more complicated than just writing a "/". Then come 
back to VHDL and write your model completely new. Do not use VHDL to 
implement an algorithm. You will fail with that (everybody would fail 
with that, but some people persistently reject to understand that). 
Develop an architecture first, and then use VHDL to describe this 
architecture.

von Lothar M. (Company: Titel) (lkmiller) (Moderator)


Rate this post
useful
not useful
Vancouver wrote:
> use VHDL to describe this architecture.
Just to confirm that statement. VHDL ist NOT a programming language. 
Its a hardware description language. So there must be a kind of 
"picture" (at least in brain) that can be described with VH*D*L.
Afer syntheszing your desgin have a look for the RTL-schematics and chek 
whether it matches your "picture" or not. If not, then you have to 
change your VHDL description until its alsmost the same.

> not for unpipelined MULT
The SPARTAN 3 FPGA on the eval board has hardware multipliers that can 
handle up to 18x18 bit data width: 
https://www.xilinx.com/support/documentation/application_notes/xapp467.pdf
With the description above and its 4 bit input they can be used and the 
20MHz will cause no problem here.

>>> (A_int / B_int)
> division in hardware is much more complicated than just writing a "/"
Indeed!

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.