In the first part of this tutorialwe started with a simple implementation of an LFSR block (Chapter 1) and its test bench (Chapter 2). Let’s make our code look a bit more professional.
Chapter 3 – Upgrading the LFSR code
Good code doesn’t use hard-coded constants as were used on the first part (to define the LFSR width). The downside of using constants is that code updates and maintenance is cumbersome at best. If we want to change the width of the register… we must scan the code and change each and every instance of the constant. There is a great possibility of making mistakes while doing that. Forgetting to change one of the ‘3’s… or changing one that was not related to the register width.
This is not obviously seen in a short piece of code, but as our code gets longer, maintaining hard-coded constants is a sure recipe for trouble.
Instead of using hard-coded constants, it is preferable to use symbols that represent those constant values.
VHDL gives us many ways of defining constants. We can define it in the architecture body, we can define them as GENERICS, or we can define them in a predefined package. In this tutorial we will use this last option.
This new version (v1.1) of the pseudo-random generator includes a predefined constants package (at this stage it contains only one constant), as shown below:
[CODE]library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.ALL;
package lfsr_pkg is
constant LFSR_W : natural := 11; — LFSR width
end lfsr_pkg;
[/CODE]
Besides the usage of constants in a package, the following features were introduced in this version:
[LIST=1]
[CODE]library ieee;
use ieee.std_logic_1164.all;
use work.lfsr_pkg.all;
entity lfsr1_1 is
port (
reset : in std_logic;
clk : in std_logic;
enable : in std_logic; — Enable shifting
count : out std_logic_vector (LFSR_W-1 downto 0) — lfsr output
);
end entity;
architecture rtl of lfsr1_1 is
signal count_i : std_logic_vector (LFSR_W-1 downto 0);
signal feedback : std_logic;
begin
feedback <= not(count_i(LFSR_W-1) xor count_i(LFSR_W-3)); — LFSR size 11
process (reset, clk)
begin
if (reset = ‘1’) then
count_i ‘0’);
elsif (rising_edge(clk)) then
if (enable = ‘1’) then
count_i <= count_i(LFSR_W-2 downto 0) & feedback;
end if;
end if;
end process;
count <= count_i;
end architecture;
[/CODE]
Notes:
a- If you look carefully at the waveform below, you will be able to see that this signal is also periodic. However, its period is much longer than that of the block analyzed in chapters 1 and 2.
b- As an exercise (and before you read the next chapter), you can try to change the test bench for this new version. It can be verified that the new sequence has 511 values, so the period of this pseudo random signal is 10220ns (clk is 50MHz => 20ns for each value x 511 values)
Chapter 4 – Saving the simulation data output to a file
In this chapter we will see how to update our test bench to include the changes done on Chapter 3, but we will also add data saving capabilities. In this way we can:
- Compare the VHDL data output with data generated by our reference design (in our case, a Matlab algorithm)
- Analyze the data output with other tools (again, in our case, we will use Matlab to produce FFT analysis of the block output).
If you still didn’t make the changes of the test bench for Chapter 3 by yourself, do not continue reading. Try doing those changes first.
On the following paragraphs I have put only the changes on the code from previous versions. At the end of this chapter you will be able to find a link to the complete files with all the code.
On the test bench, a new process was added for data saving. Here is the code fragment which includes this new process:
[CODE]— Save data to file
process
file file_id: text;
variable line_num: line;
variable cnt: integer := 0;
begin
— Open the file
file_open(file_id, log_file, WRITE_MODE);
wait until (enable = ‘1’);
wait until (clk = ‘1’);
— Loop and write all values to a file
for cnt in 0 to 2047 loop
write(line_num, to_integer(unsigned(count)) );
writeline(file_id, line_num);
wait until (clk = ‘1’);
end loop;
file_close(file_id);
endSim <= true;
wait until (clk = ‘1’);
end process;
[/CODE]
First part of the process: Open a file for data saving. Wait until enable and clkare asserted.
Data writing loop: The data is converted to unsigned, then to integer (*), and then it is written to a “line”. The “line” is then written to the file. One write is done for each clock rising flank.
After the loop is completed: Close the data archive. Activate the endSim flag that finishes the simulation run.
(*) VHDL supports algebraic operations over std_logic_vector signals. But first it has to know how to interpret the vector, either as signed or unsigned.
The test bench dumps the output data to a file called res.log. The file begins like this:
[CODE]0
1
3
7
15
31
63
127[/CODE]
… and as it was expected, it has 2047 entries until it repeats itself. In the next chapter we will see how to check these values using Matlab.
References
LFSR design – Xilinx application note
Signed and unsigned in VHDL
Pseudo random generator tutorial part 1
Pseudo random generator tutorial part 3
My blog: FPGA Site
P.S. The files for chapters 3 and 4 are available below. Please rename the .txt extension to .vhd prior to simulation or synthesis
TSMC 16th OIP Ecosystem Forum First Thoughts