0
\$\begingroup\$

I'm trying to write data into an instance of the Xilinx Memory Interface Generator that I receive from a UART. I'm using VHDL in Vivado.

The UART presents data 8 bits at a time, with quite a few clock cycles between each fresh data. The MIG data input is a 512-bit vector.

signal s_app_wdf_data : STD_LOGIC_VECTOR(511 downto 0); -- Output to MIG
signal s_uart_data    : STD_LOGIC_VECTOR(7 downto 0); -- input from UART

I wish to populate the vector with data received from the UART. I will update each part of the vector and then when I get the last byte from the UART I will enable the write signals to the MIG.

I created a counter to keep track of what position in the vector was the relevant position for UART data to be written to, so every time there is fresh data on the UART do something like this:

s_app_wdf_data(v_position downto v_position - 7) <= s_uart_data;
v_position := v_position - 8;

However, this took about an hour to do route_design, and gave timing errors (negative slack).

I was thinking that in order for that to work then the tools would have to synthesise something that essentially allows random access to any part of the vector, so maybe I could help clarify to the tools that only certain parts of the vector would ever be accessed since the position only ever changes in decrements of 8. I wrote out

if (v_position = 511) then
    s_app_wdf_data(511 downto 504) <= s_uart_data;
elsif (v_position = 503) then
    s_app_wdf_data(503 downto 496) <= s_uart_data;
elsif (v_position = 495) then
    s_app_wdf_data(495 downto 488) <= s_uart_data;
etc, etc

It was laborious to type out (a clear sign something is wrong), and in the end had even worse timing errors.

Next I tried to avoid code that would access arbitrary places in the vector, I tried using shifting and updating just the lowest byte.

s_app_wdf_data(511 downto 8) <= s_app_wdf_data(503 downto 0);
s_app_wdf_data(7 downto 0) <= s_uart_data;

This one had the worst Total Negative Slack of the lot.

What is the correct approach here? Or what techniques should I be researching?

\$\endgroup\$
  • \$\begingroup\$ First, your if-then-else tree is a "priority encoder", and if it is as long as all 64 slices, you have at least 64 levels of logic on that path... if you look at the syn report, you will see that. So, you likely need to get rid of that. Can you add some details as to what you are doing... are you trying to write byte-wide to memory? Are you going to write every byte to memory? Most importantly, have you drawn this design out on paper/visio/etc. before writing RTL? if not, I highly recommend you do this, so you can see, long before you write RTL, what you are going to implement. \$\endgroup\$ – CapnJJ May 21 '18 at 23:08
0
\$\begingroup\$

I have some code here that does something extremely similar to what you're trying to implement:

https://github.com/alexforencich/verilog-wishbone/blob/master/rtl/axis_wb_master.v

This code connects an AXI stream interface (which could be connected to a UART, ethernet interface, etc.) to a wishbone memory bus to issue read/write requests on the bus.

This code uses the verilog indexed part select method to update the data register like so:

data_next[AXIS_DATA_WORD_SIZE*count_reg +: AXIS_DATA_WORD_SIZE] = input_axis_tdata;
count_next = count_reg + 1;

I am not super familiar with VHDL, but perhaps you can try a small modification of one of the techniques you've already tried:

s_app_wdf_data(v_position*8 downto v_position*8 - 7) <= s_uart_data;
v_position := v_position - 1;

What you want is for the synthesizer to set up the output register such that the input data word is routed to each possible set of 8 output bits, and then generating one byte enable per offset. If you ensure that there can never, ever be any overlap, then this ends up being very efficient. In the code you originally tried, it seems that maybe the synthesizer wasn't able to determine that there weren't any overlaps and as a result it produced a very sub-optimal result.

\$\endgroup\$
0
\$\begingroup\$

In general, think parallel/concurrent processing. Is there anything preventing you from writing each received "data sized to your memory width" before receiving all 64 bytes? If not, write to the memory location each time you receive each "properly sized data" from the UART. That way you are only dealing with a single slice of the 512 bits at any given time. Likely, you are using your memory as a FIFO(?), so it is a simple write and read pointer that is your memory address (relative or absolute, either way).

If you stop and think about it, in terms of HW, you are already "Storing" all 64 bytes before writing memory anyway... and, to be sure, "storage" can be registers or memory, as you please... so, I think if you stop trying to register 512 bits and then write the slices to memory, and instead store it in memory "on the fly", your design will meet timing, and fit your device more easily.

\$\endgroup\$
0
\$\begingroup\$

I'll be honest I'm surprised that what you describe doesn't work, as it is not an unusual pattern.

Having said that, here's a thought: given the UART will be really slow compared to your system clock, can you just shift the UART data one bit at a time into the MIG vector:

for i in range 0 to 7 loop
  s_app_wdf_data(s_app_wdf_data'high downto 1) <= s_app_wdf_data(s_app_wdf_data'high-1 downto 0) & s_uart_data(s_uart_data'high);
  s_uart_data <= s_uart_data(s_uart_data'high-1 downto 0) & '0';
  wait until rising_edge(clk)
end loop;

Or something like that :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.