VHDL-verification
Package to ease directed testing of HDL entities
uart_emu.vhd
Go to the documentation of this file.
1 -------------------------------------------------------------------------------
2 --! @file
3 --! @author Hipolito Guzman-Miranda
4 --! @brief Emulates an UART receiver.
5 -------------------------------------------------------------------------------
6 
7 --! Use IEEE standard definitions library
8 library ieee;
9 --! Use std_logic* signal types
10 use ieee.std_logic_1164.all;
11 --! Use arithmetic operations and unsigned types
12 use ieee.numeric_std.all;
13 --! Use the chr function to convert integer to char
14 use work.txt_util.all;
15 --! Include datatypes and procedures needed to write to files
16 use std.textio.all;
17 --! Use the slv2string function present in the vhdl_verification package
18 use work.vhdl_verification.all;
19 
20 --! @brief Emulates an UART receiver. Writes receiver characters to a file.
21 --!
22 --! @details Waits for \c uart_input to leave rest state, and extracts data
23 --! from the serial line, performing some sanity checks. If \c PARITY is either
24 --! "even" or "odd", parity is also extracted from the serial line and checked
25 --! against the expected value. In the case of a parity mismatch, an error will be
26 --! printed in the simulation log. Received characters will be printed in \c
27 --! OUTPUT_FILE
28 --! state of the leds each time any the input bit changes. Does not keep any
29 --! internal memory, \c led_input is assumed to be directly connected to the
30 --! leds.
31 entity uart_emu is
32  generic (
33  OUTPUT_FILE : string := "uart.log"; --! File where received chars will be written
34  VERBOSE : boolean := false; --! Log beginning and end of transactions, as well as individual bit values
35  DATA_BITS : integer := 8; --! Number of data bits in the UART word
36  PARITY : string := "none"; --! Can be either "even", "odd", or "none"
37  BIT_DURATION : time := 104 us; --! Duration of each bit, depends on baudrate (it is actually 1 second / baudrate)
38  STOP_BITS : integer := 1 --! Can be either 1 or 2
39  );
40  port (
41  uart_input : in std_logic --! Connect here the TX signal from your design
42  );
43 end uart_emu;
44 
45 --! @brief Architecture is based on just a single process
46 --!
47 --! @detailed An assertion has also been added to check that generics are
48 --! assigned supported values.
49 architecture uart_emu_arch of uart_emu is
50 
51  constant LINE_FEED : std_logic_vector(7 downto 0) := x"0A";
52 
53  signal rxparity : std_logic;
54  signal expectedparity : std_logic;
55 
56 begin
57 
58  -- PARITY must be one of the three supported values
59  assert PARITY = "even" or PARITY = "odd" or PARITY = "none"
60  report "uart_emu: PARITY must be either even, odd or none"
61  severity failure;
62 
63  -- STOP_BITS must be one of the two supported values
64  assert STOP_BITS = 1 or STOP_BITS = 2
65  report "uart_emu: STOP_BITS must be either 1 or 2"
66  severity failure;
67 
68 
69  --! @brief Decode \c uart_input signal and write it to \c OUTPUT_FILE
70  --!
71  --! @detailed Assuming the line is in the rest value ('1') when we are
72  --! outside of the process, when we enter the process it should be because
73  --! the line changed to the start bit ('0'). From that point, get all the
74  --! data values and the parity, performing some sanity checks.
75  --! Please note that this process does not check for signal stability
76  --! (we will not raise any errors if the signals change a bit sooner or later
77  --! than \c BIT_DURATION), we just measure the signal at half the bit time,
78  --! as a real UART receiver would do.
79  get_values: process
80 
81  file f : text;
82  variable l : line;
83  variable rxdata : std_logic_vector(DATA_BITS-1 downto 0);
84 
85  begin
86 
87  -- A transaction start when the line goes to a strong low level
88  wait until uart_input = '0';
89  if VERBOSE then
90  report "uart_emu: uart transaction begin";
91  end if;
92 
93  -- Offset half a bit so we measure at the center of each bit
94  wait for BIT_DURATION/2;
95 
96  -- Raise an error if we are not in the start bit
97  if (uart_input /= '0') then
98  report "uart_emu: wrong value for start bit: expected: '0', actual: " & std_logic'image(uart_input) severity failure;
99  end if;
100 
101  -- Get all data bits
102  for i in 0 to DATA_BITS-1 loop
103  wait for BIT_DURATION;
104  rxdata(i) := uart_input;
105  if VERBOSE then
106  report "uart_emu: sampled bit " & integer'image(i) & ", got value: " & std_logic'image(uart_input);
107  end if;
108  end loop;
109 
110  -- Print received data. If DATA_BITS = 8, print it also in ASCII
111  if DATA_BITS = 8 then
112  report "uart_emu: received data: " & slv2string(rxdata) & " (bin), " & slv2hexstring(rxdata) & " (hex), " & ascii(to_integer(unsigned(rxdata))) & " (ASCII)";
113  else
114  report "uart_emu: received data: " & slv2string(rxdata) & " (bin), " & slv2hexstring(rxdata) & " (hex)";
115  end if;
116 
117  -- If DATA_BITS = 8, write received data to output file. Data is always
118  -- prepared to be written to the output file, no matter if we have parity
119  -- errors or not.
120  --
121  -- We write the received char into the line variable, and not directly
122  -- into the file, unless we got a newline char (line feed, \n). Only when
123  -- we receive a newline char, we commit the line to the file.
124  --
125  -- This would probably be more convenient if we wrote to a pipe instead
126  -- of writing to a file, but I am not sure if that would work on Windows.
127  --
128  -- Since the file output seems to be buffered, normally it would not be
129  -- actually written to the log until we close the file, which should
130  -- happen automatically at the end of the simulation. But in that case
131  -- we would get all the text at the same time, instead of line by line as
132  -- the simulation progresses. So we will open and close the file each
133  -- time we have a complete line.
134  if DATA_BITS = 8 then
135  if rxdata /= LINE_FEED then
136  write (l, ascii(to_integer(unsigned(rxdata))));
137  else
138  file_open(f, OUTPUT_FILE, append_mode);
139  writeline (f, l);
140  file_close(f);
141  end if;
142  end if;
143 
144  -- If parity is even or odd, get parity bit
145  if PARITY /= "none" then
146  wait for BIT_DURATION;
147  rxparity <= uart_input;
148  end if;
149 
150  -- Calculate expected parity
151  if PARITY = "even" then
152  expectedparity <= xor_reduce(rxdata);
153  elsif PARITY = "odd" then
154  expectedparity <= xnor_reduce(rxdata);
155  else
156  expectedparity <= '-';
157  end if;
158 
159  -- Check if received parity matches expected, report an error if not
160  if PARITY /= "none" then
161  if rxparity /= expectedparity then
162  report "uart_emu: parity error! received parity bit: " & std_logic'image(rxparity) & " expected: " & std_logic'image(expectedparity)
163  severity error;
164  end if;
165  end if;
166 
167  -- Wait for stop bits, and raise errors if the value is incorrect.
168  -- We always will have at least one stop bit
169  wait for BIT_DURATION;
170  if (uart_input /= '1') then
171  report "uart_emu: wrong value for stop bit: expected: '1', actual: " & std_logic'image(uart_input) severity failure;
172  end if;
173 
174  -- But sometimes we will have a second stop bit
175  if STOP_BITS = 2 then
176  wait for BIT_DURATION;
177  if (uart_input /= '1') then
178  report "uart_emu: wrong value for second stop bit: expected: '1', actual: " & std_logic'image(uart_input) severity failure;
179  end if;
180  end if;
181 
182  -- We could do a last half-bit shift before we exit the process, but since
183  -- we will wait for an event on uart_input when we enter the process again,
184  -- that half-bit wait will be performed nevertheless.
185  -- Also, by not manualy waiting for a half-bit, we avoid missing the start
186  -- bit event on uart_input in the case of receiving a new transaction just
187  -- after the last stop bit.
188 
189  if VERBOSE then
190  report "uart_emu: uart transaction end";
191  end if;
192 
193  end process;
194 
195 end uart_emu_arch;
in uart_inputstd_logic
Connect here the TX signal from your design.
Definition: uart_emu.vhd:42
Common datatypes and component declarations.
PARITYstring := "none"
Can be either "even", "odd", or "none".
Definition: uart_emu.vhd:36
STOP_BITSinteger := 1
Can be either 1 or 2.
Definition: uart_emu.vhd:39
OUTPUT_FILEstring := "uart.log"
File where received chars will be written.
Definition: uart_emu.vhd:33
uart_emu
Connect here the signal that drives the leds.
DATA_BITSinteger := 8
Number of data bits in the UART word.
Definition: uart_emu.vhd:35
VERBOSEboolean := false
Log beginning and end of transactions, as well as individual bit values.
Definition: uart_emu.vhd:34
BIT_DURATIONtime := 104 us
Duration of each bit, depends on baudrate (it is actually 1 second / baudrate)
Definition: uart_emu.vhd:37
Emulates an UART receiver. Writes receiver characters to a file.
Definition: uart_emu.vhd:31
Allows for text output in simulation.
Definition: txt_util.vhd:12