I want do drive a 4x16 LCD display using VHDL. The first line should say "FREQ: 000 RPM" where the zeroes represent incoming 8-bit frequency data. Same for the next lines with different data, also 8-bit. My VHDL code is as follows:
-- 16 Characters
subtype string16_type is string(1 to 16);
-- 4 string of 16 characters
type message4x16_type is array(1 to 4) of string16_type;
-- Define messages displayed
constant message_home: message4x16_type := ( --1234567890123456
1 => "FREE MODE ",
2 => "PARCOURS ",
3 => "- - - - - - - - ",
4 => " - - - - - - - -");
constant message_info: message4x16_type := ( --1234567890123456
1 => "FREQ: 000 RPM ",
2 => "SPEED: 000 KM/H ",
3 => "DIST: 000 KM ",
4 => "MORE INFO ");
-- Character amount in line
signal character_counter: integer range 1 to 16;
-- Line amount on LCD
signal line_counter : integer range 1 to 4;
Then follows a state machine, with state write_char looking partly like this:
if msg_select = '0' then
aline := message_home(line_counter);
elsif msg_select = '1' then
aline := message_info(line_counter);
end if;
data <= std_logic_vector(to_unsigned(character'pos(aline(character_counter)),8));
Everything works smoothily this way but I can't think of a way to implement the frequency data into the message, like using %i in C. I am aware of the 'record' statement but not sure how to use it in this situation. Any other ways to implement the data are very welcome.
Thanks on forehand.
Declaring types, constants and signals as before:
-- 16 characters
type lcd_line_type is array(0 to 15) of character;
-- 4 lines of 16 characters
type message4x16_type is array(0 to 3) of lcd_line_type;
-- Define messages displayed
constant message_home : message4x16_type := (
--1234567890123456
"FREE MODE ",
"PARCOURS ",
"- - - - - - - - ",
" - - - - - - - -"
);
constant message_info : message4x16_type := (
--1234567890123456
"FREQ: 000 RPM ",
"SPEED: 000 KM/H ",
"DIST: 000 KM ",
"MORE INFO "
);
-- Character amount in line
signal character_counter : integer range 0 to 15;
-- Line amount on LCD
signal line_counter : integer range 0 to 3;
subtype rawchar is std_logic_vector(7 downto 0);
type rawstring is array(natural range <>) of rawchar;
signal rpm : rawstring(2 downto 0);
signal kmh : rawstring(2 downto 0);
function to_rawchar(char : character) return rawchar is
begin
return std_logic_vector(to_unsigned(character'pos(char), 8));
end function;
Usage example:
if msg_select = '0' then
data <= to_rawchar(message_home(line_counter)(character_counter));
elsif msg_select = '1' then
case line_counter is
when 0 =>
-- replace 000 by rpm(2:0)
case character_counter is
when 6 => data <= rpm(2);
when 7 => data <= rpm(1);
when 8 => data <= rpm(0);
when others => data <= to_rawchar(message_info(0)(character_counter));
end case;
when 1 =>
-- replace 000 by kmh(2:0)
case character_counter is
when 7 => data <= kmh(2);
when 8 => data <= kmh(1);
when 9 => data <= kmh(0);
when others => data <= to_rawchar(message_info(1)(character_counter));
end case;
-- ...
end case;
end if;
The first case tests for the requested line. The second case overrides the constant values at particular positions.
I additionally extracted the char to slv conversion into a function.
Related
I would like to create an array and access it in the following way for read and write slice operations (i.e. more than one element at once):
If the indices are within range access them as usual
If the second index is smaller than the first index, access the data as follows:
First .. A'Last & A'First .. (First + 5) (turns out this doesn't work as-is due to concatenation result upper bound out of range)
I have come up with the following example to demonstrate the issue:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Test_Modular is
type Idx is mod 10;
type My_Array is array (Idx range <>) of Integer;
A: My_Array(Idx) := (
0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5,
6 => 6, 7 => 7, 8 => 8, 9 => 9
);
First: constant Idx := 7;
S: constant My_Array := A(First .. First + 5);
begin
for I in S'range loop
Put_Line(Idx'Image(I) & " --> " & Integer'Image(S(I)));
end loop;
end Test_Modular;
As in the example, the 5 is static, the compiler warns me as follows:
$ gnatmake -o test_modular test_modular.adb
x86_64-linux-gnu-gcc-10 -c test_modular.adb
test_modular.adb:18:19: warning: loop range is null, loop will not execute
x86_64-linux-gnu-gnatbind-10 -x test_modular.ali
x86_64-linux-gnu-gnatlink-10 test_modular.ali -o test_modular
When running the program, I observe the following:
$ ./test_modular
i.e. no output as predicted by the compiler warning.
Now I wonder: Is there a way to write the slice like A(First .. First + 5) and make it “wrap around” such that the data accessed will be the same as in this modified example program except without having to distinguish the two cases in the code explicitly?
with Ada.Text_IO;
use Ada.Text_IO;
procedure Test_Modular_2 is
type Idx is mod 10;
type My_Array is array (Idx range <>) of Integer;
A: My_Array(Idx) := (
0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5,
6 => 6, 7 => 7, 8 => 8, 9 => 9
);
First: constant Idx := 7;
S1: constant My_Array := A(First .. A'Last);
S2: constant My_Array := A(A'First .. (First + 5));
begin
for I in S1'range loop
Put_Line(Idx'Image(I) & " --> " & Integer'Image(S1(I)));
end loop;
for I in S2'range loop
Put_Line(Idx'Image(I) & " --> " & Integer'Image(S2(I)));
end loop;
end Test_Modular_2;
Try the following approach:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
type Idx is mod 10;
type My_Array is array (Idx range <>) of Integer;
function rotate (arr : My_Array; head : Idx; tail : Idx) return My_Array is
P1_Len : Natural;
P2_Len : Natural;
begin
P1_Len :=
(if head <= tail then Natural (tail) - Natural (head) + 1
else Natural (arr'Last) - Natural (head) + 1);
P2_Len := (if head <= tail then 0 else Natural (tail) + 1);
declare
Result : My_Array (0 .. Idx (P1_Len + P2_Len - 1));
begin
if head <= tail then
Result := arr (head .. tail);
else
Result (0 .. Idx (P1_Len - 1)) := arr (head .. arr'Last);
Result (Idx (P1_Len) .. Result'Last) := arr (0 .. tail);
end if;
return Result;
end;
end rotate;
procedure print (A : My_Array) is
begin
for V of A loop
Put (V'Image);
end loop;
New_Line;
end print;
A : My_Array :=
(0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8,
9 => 9);
head : Idx := 7;
tail : Idx := head + 5;
begin
Put_Line ("Head: " & head'Image & " Tail:" & tail'Image);
Put_Line ("Initial value order:");
print (A);
declare
S1 : My_Array := rotate (A, head, tail);
begin
Put_Line ("Rotated value order:");
print (S1);
end;
end Main;
The rotate function above rotates the set of values indicated by the head and tail index values, placing the head value at the start of the array returned by rotate. This example shows that the resulting array may contain fewer elements than the array passed as a parameter to the function.
Given that Idx is a modular type, an alternative approach is
function Rotate (Source : in My_Array; Start : in Idx; Stop : in Idx) return My_Array is
Last : Idx;
Next : Idx := Start;
Result : My_Array (Idx);
begin -- Rotate
Copy : for I in Result'range loop
Result (I) := Source (Next);
Last := I;
exit Copy when Next = Stop;
Next := Next + 1;
end loop Copy;
return Result (0 .. Last);
end Rotate;
A further alternative with explicit result-array bounds calculation could be:
function Get_Wrapped_Slice(A: in My_Array;
Start: in Idx;
Stop: in Idx) return My_Array is
Length: constant Idx := (if Stop >= Start then (Stop - Start) else (A'Last - Start + Stop + 1));
Result: My_Array( 0 .. Length);
Cursor: Idx := Start;
begin
for I of Result loop
I := A(Cursor);
Cursor := Cursor + 1;
end loop;
return Result;
end Get_Wrapped_Slice;
How do you use record arrays in generate statements in VHDL? Below is what I have done, which passes synthesis and implementation in Vivado (2021.1). However, when I simulate it, it shows none of the record signals getting assigned and are always "U". I can also not find any examples of record fields used in port maps in generate statements.
type record_a_t is record
a : std_logic;
b : std_logic_vector(7 downto 0);
c : std_logic_vector(3 downto 0);
d : std_logic;
e : std_logic;
end record record_a_t;
type record_a_array_t is array (0 to 3) of record_a_t;
signal record_a_array : record_a_array_t;
Then, I generate the modules with signal mapping like:
GEN_MODULES : for i in 0 to 3 generate
modules : entity work.module
port map(
clk => clk,
rst_n => rst_n,
a => record_a_array(i).a,
b => record_a_array(i).b,
c => record_a_array(i).c,
d => record_a_array(i).d,
e => record_a_array(i).e
);
end generate GEN_MODULES;
Again, the above seems to pass all the tools syntax checking and builds in Vivado, but simulation (RivPro 2018) and implementation show signals not getting assigned or changing. They are always "U", even if driven from the module. For example, if port a is an output that is initialized in the module, record_a_array(i).c is always "U". Even the module output port is "U", as if the internal initialization is ignored, which makes no sense to me.
Below is an example.
Test Module:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity module is
port(
clk : in std_logic;
rst_n : in std_logic;
a : in std_logic;
b : out std_logic_vector(7 downto 0);
c : out std_logic_vector(3 downto 0);
d : out std_logic;
e : out std_logic
);
end module;
architecture rtl of module is
begin
process(clk, rst_n)
begin
if(rst_n = '0') then
b <= (others=> '0');
c <= (others=> '0');
d <= '0';
e <= '0';
elsif(rising_edge(clk)) then
if(a = '1') then
b <= b + '1';
c <= c + '1';
d <= not d;
e <= not e;
end if;
end if;
end process;
end rtl;
Test Bench:
library ieee;
use ieee.std_logic_1164.all;
entity testbench is
end entity testbench;
---------------------------------------------------
architecture arch of testbench is
---------------------------------------------------
type record_a_t is record
a : std_logic;
b : std_logic_vector(7 downto 0);
c : std_logic_vector(3 downto 0);
d : std_logic;
e : std_logic;
end record record_a_t;
type record_a_array_t is array (0 to 3) of record_a_t;
signal record_a_array : record_a_array_t;
signal clk : std_logic := '1';
signal rst_n : std_logic := '0';
---------------------------------------------------
begin
clk <= not clk after 5 ns;
rst_n <= '1' after 20 ns;
GEN_MODULES : for i in 0 to 3 generate
modules : entity work.module
port map(
clk => clk,
rst_n => rst_n,
a => record_a_array(i).a,
b => record_a_array(i).b,
c => record_a_array(i).c,
d => record_a_array(i).d,
e => record_a_array(i).e
);
end generate GEN_MODULES;
process(clk, rst_n)
begin
if(rst_n = '0') then
for i in 0 to 3 loop
record_a_array(i).a <= '0';
end loop;
elsif(rising_edge(clk)) then
for i in 0 to 3 loop
record_a_array(i).a <= not record_a_array(i).a;
end loop;
end if;
end process;
end arch;
Simulation:
Simulation of module example
Notice how the record signals wired to the module outputs in at the TestBench level are never defined and how the outputs of the generated module instances themselves are not driven, even though they all get asserted in the reset condition. The only signal that gets driven is the 'a' signal from the test bench level.
Since the record is not crossing into the RTL space, unwanted drivers can be turned off using std_logic using 'Z':
constant REC_INIT : record_a_t := (
a => 'Z',
b => "ZZZZZZZZ",
c => "ZZZZ",
d => 'Z',
e => 'Z'
) ;
. . .
signal record_a_array : record_a_array_t := (0 to 3 => REC_INIT) ;
As you start to look at the issue in the LRM, you might also note you can find the issues the IEEE VHDL working group has recorded at: : https://gitlab.com/IEEE-P1076/VHDL-Issues/-/issues. There is a remotely related issue in https://gitlab.com/IEEE-P1076/VHDL-Issues/-/issues/275 that may be able to address both its issue and this one - though time will tell on that.
For verification, I use Open Source VHDL Verification Methodology (OSVVM). We use records on ports and as a result, use special resolution functions with them so they do not require initialization.
I'm new to VHDL and I have a problem I don't know how to resolve.
The problem appears when I'm doing the simulation, but the code compiles correctly.
Here is the problem :
entity fir is
port ( clock : in STD_LOGIC;
reset : in STD_LOGIC;
X : in STD_LOGIC_VECTOR (7 downto 0); -- X = X(n)
Y : out STD_LOGIC_VECTOR (16 downto 0)); -- Y = Y(n)
end fir;
architecture Behavioral of fir is
signal X_s, XZ1, XZ2, XZ3, XZ4, XZ5, XZ6, XZ7 : signed (7 downto 0); -- XZi = X(n-i)
signal Y_s : signed (16 downto 0);
constant A : signed(7 downto 0) := "00001011"; -- A=11
(constant B,C,D,E,F,G,H similary to A)
begin
process (clock, reset)
begin
X_s <= signed(X);
if (rising_edge(clock)) then
if (reset = '1') then
X_s <= (others => '0');
Y_s <= (others => '0');
XZ1, XZ2,XZ3...XZ7 <= (others => '0'); <= (others => '0');
else
XZ1 <= X_s;
XZ2 <= XZ1;
XZ3 <= XZ2;
XZ4 <= XZ3;
XZ5 <= XZ4;
XZ6 <= XZ5;
XZ7 <= XZ6;
end if;
end if;
end process;
Y_s <= (X_s*A) + (XZ1*B) + (XZ2*C) + (XZ3*D) + (XZ4*E) + (XZ5*F) + (XZ6*G) + (XZ7*H);
Y <= std_logic_vector(Y_s);
end Behavioral;
This line :
Y_s <= (X_s*A) + (XZ1*B) + (XZ2*C) + (XZ3*D) + (XZ4*E) + (XZ5*F) + (XZ6*G) + (XZ7*H);
returns this error : "array shape mismatch - no matching element"
I firstly think it was because of its size, but the problem is still there even if I replace this line :
Y_s <= (X_s*A) + (XZ1*B) + (XZ2*C) + (XZ3*D) + (XZ4*E) + (XZ5*F) + (XZ6*G) + (XZ7*H);
by this one :
Y_s <= (X_s*A);
However, it works with this line : (just to see if the rest of the code is correct) :
Y_s <= (others => '0');
What could I do ?
Thanks a lot.
Your simple case:
Y_s <= (X_s*A);
Y_s - 17 wires (16 downto 0). X_s - 8 wires (7 downto 0). A - 8 wires (7 downto 0). 8 + 8 != 17.
Complex sum:
Y_s <= (X_s*A) + (XZ1*B) + (XZ2*C) + (XZ3*D) + (XZ4*E) + (XZ5*F) + (XZ6*G) + (XZ7*H);
Result sizes for addition and multiplication:
| Operation | Size of result |
+------------+------------------------------+
| Y <= A + B | Maximum (A'Length, B'Length) |
| V <= A * B | A'Length + B'Length |
Therefore your whole sum has size 16: Maximum(8+8,8+8,8+8,8+8,8+8,8+8,8+8,8+8).
Section "“Signed”/“Unsigned” Addition without Overflow" on this page explains how to solve this.
Properly sizing Y_s should solve your problem.
You also should move the assignment X_s <= signed(x) outside the process statement and remove X_s<= (others => '0') assignment inside the reset condition.
I might be new to synthesizing loops. I am trying to create a counting sort in VHDL which takes arbitary length(say N) of integer array as input for sorting.
The code for design heavily relies on for loop. The looping logic seems to be parallel within a process or may be across processes. So I put all of the for loop in one process. But with that, the loops seem to execute in parallel independently. Which defeats the purpose as one loop should follow the other.
I have used counting sort algorithm from https://www.geeksforgeeks.org/counting-sort/
I am unable to get the logic working as mentioned in the algorithm.
Q1: How does the for loop execute within a process. Does all the for loop execute in parallel.
Q2:Is there an alternate way of implementing this for loop logic in VHDL, so that it executes serially?
Q3:Are there limitations for synthesizing for loops?
Design code:
library ieee;
use ieee.std_logic_1164.all;
package sorting_pkg is
type intarray is array(natural range <>) of std_logic_vector(7 downto 0);
end package;
library ieee;
use ieee.std_logic_1164.all;
use work.sorting_pkg.all;
use IEEE.NUMERIC_STD.all;
use ieee.std_logic_unsigned.all;
entity Sorting is
generic (
N : integer );
port(
--clk : in STD_LOGIC;
rst : in STD_LOGIC;
inStream : in intarray(0 to N-1);
outStream : out intarray(0 to N-1);
BoutArray: out intarray(0 to 16);
CoutArray: out intarray(0 to 16)
);
end entity;
architecture behavior of Sorting is
signal BArray: intarray(0 to 16) := (others => "00000000");
signal CArray: intarray(0 to 16) := (others => "00000000");
signal DArray: intarray(0 to 16) := (others => "00000000");
--signal Aindex1,Aindex2,Cindex,k: std_logic_vector(7 downto 0);
--signal Aindex1,Aindex2,Cindex,k : natural range 0 to 16;
begin
process
begin
if(rst = '1') then
outStream <=(others => "00000000");
else
Bloop: for i in 0 to N-1 loop
BArray(to_integer(unsigned(inStream(i)))) <=BArray(to_integer(unsigned(inStream(i)))) + 1;
end loop;
CArray(0) <= BArray(0);
Cloop: for j in 1 to 16 loop
CArray(j) <= BArray(j) + CArray(j - 1);
end loop;
Coutloop:for l in 0 to N-1 loop
DArray(to_integer(unsigned(inStream(l)))) <= CArray(to_integer(unsigned(inStream(l)))) - 1;
end loop;
outloop:for m in 0 to N-1 loop
outStream(to_integer(unsigned(DArray(to_integer(unsigned(inStream(m))))))) <= inStream(m);
end loop;
BoutArray <= BArray;
CoutArray <= DArray;
end if;
wait;
end process;--end proc
end architecture;
Testbench code:
library ieee;
use ieee.std_logic_1164.all;
library work;
use work.sorting_pkg.all;
use IEEE.NUMERIC_STD.all;
use ieee.std_logic_unsigned.all;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity tb_camera is
-- Port ( );
end tb_camera;
architecture Behavioral of tb_camera is
component Sorting
generic (
N : integer := 10);
PORT
(--clk: in std_logic;
rst : in std_logic;
inStream : in intarray(0 to 9);
outStream : out intarray(0 to 9);
BoutArray: out intarray(0 to 16);
CoutArray: out intarray(0 to 16)
);
end component;
signal A : intarray(0 to 9);
signal D : intarray(0 to 9);
signal rst_tb : std_logic;
-- signal clk_tb : std_logic;
signal BoutArray : intarray(0 to 16);
signal CoutArray : intarray(0 to 16);
--constant clk_period : time :=500ns;
begin
uut:Sorting port map (inStream => A, outStream => D, rst => rst_tb,
BoutArray => BoutArray, CoutArray=> CoutArray );
sim_tb:process
begin
wait for 100ns;
rst_tb<='1';
wait for 100ns;
rst_tb<='0';
--wait for 50ns;
A(0 to 9) <= (0 => x"07",
1 => x"09",
2 => x"06",
3 => x"02",
4 => x"05",
5 => x"00",
6 => x"08",
7 => x"01",
8 => x"03",
9 => x"04");
wait;
end process;
end Behavioral;
I'm trying to create a program that will take an input of two integers of size 10 and below, that counts the amount of carry operations as a result of summing the input integers.
For example:
Sample Input:
59864417 974709147
Sample Output:
6
The line which returns an error in my program is:
parta[i+1] = ( parta[i+1] + 1 )
There i'm trying to add 1 to the value currently in the next array position.
run = true
while run == true
#input string
text = gets.chomp
#split string and remove space
parta = text.split[0]
partb = text.split[1]
#convert split strings to integers
partf1 = parta.to_i
partf2 = partb.to_i
#check for terminal input of 0
if partf1 + partf2 <= 0
run = false
end
#fill strings with 0s to size 11
parta = sprintf( "%011i", parta )
partb = sprintf( "%011i", partb )
#convert strings to arrays of integers
parta = parta.split("").map(&:to_i)
partb = partb.split("").map(&:to_i)
count = 0
(10).downto(0) do |i|
if ( parta[i] + partb[i] ) > 9
count = count + 1
parta[i+1] = ( parta[i+1] + 1 )
#59864417 974709147 test input should output 6
end
end
if run == true
puts "#{count} carry operations."
end
end
When I run I get the following error message:
test5.ruby:42:in block in <main>': undefined method+' for nil:NilClass (NoMethodError)
from test5.ruby:29:in downto'
from test5.ruby:29:in
Could anyone help me? :)
Expanding my comment into a slightly longer answer:
I suspect there's a simpler approach overall, but for your particular issue: the highest index in the array is 10. In that case, when i is 10, parta[i+1] (on the right side) is nil, because there's no element in the array with that index. When you try to increment nil, you get an error. But it should be parta[i-1] = parta[i-1] + 1 anyway if you're trying to go from right to left. This will cause some odd behavior when i is 0, but you might not care about that.
The problem is on this line
parta[i+1] = ( parta[i+1] + 1 )
In case i == size of the array the expression i + 1 will return nil which causes the error.
Just for fun, here's the rewritten version with Ruby 2.4 (for Intger#digits):
a = 59_864_417
b = 974_709_147
a, b = [a, b].sort
carry = false
count = b.digits.zip(a.digits).count do |m, n|
r = m + (n || 0) + (carry ? 1 : 0)
carry = r > 9
end
p count
#=> 6