Just another site

Monthly Archives: August 2014

Control Signals For A Successful Data Exchange Between Two Different Hardware Components

For the project I’m working on, I had to interface at least one custom co-processor with a hard CPU. The problem is straight forward: two components with different clocks must be able to share information back and forth. In the absence of a unique reference, control signals must be implemented somehow to synchronize both (or more) components that work at different frequencies.

In my particular setup, the co-processor executed some data operations and dumped the results to a data bus. The CPU then performed periodic reads to the data bus in search of new results. The problem here is that the CPU wasn’t really sure if the data that was available on the bus was truly new. The CPU could read the results before the information was actually updated. Even though the co-processor was really fast, the CPU could read much faster. The situation looked like this:


Fig 1. Functional waveform of the periodic output of a custom co-processor.

As you can see in the image above, the co-processor produces periodic results through the ‘data’ bus. ‘newData’ is an internal signal and is set every time the data bus is refreshed. ‘clk01’ is the co-processor’s clock reference. Note that there’s no way for the CPU to tell if the information in the data bus has been previously read.

How can we address this issue? It took some hours to come with a satisfactory solution. Actually, the fix came in two parts. First, it is really helpful to implement ‘write request’ and ‘read request’ type signals inside the custom co-processor. The goal of these signals is to be able to identify the moment when an external component is performing data writes or data reads. Knowing this enable us to take further action.

Now, every time the CPU performed a read, it had to first acknowledge a read request to the co-processor. The second part is to use this information to “tell” the CPU that the data it is reading right now is really new. In my case, the CPU always read a word of 32 bits. Of those 32 bits I only was using 24 for data results. I decided to use one extra bit (the least significant) to identify previously read data.

How does it work? The co-processor sets the least significant bit of the word immediately after the first external read is performed. Whenever the CPU sampled a word, it could now clearly tell if it was new or old data just by looking at the least significant bit. The results looked like this:


Fig 2. Functional waveform of the periodic output of a custom co-processor after fix.

Take some time to check out the previous image. Here, I added a new read request signal (readRq). Note that this signal is asynchronous. If readRQ is set, then the least bit of word in the data bus will also be set for the next clock cycle.

In VHDL, the fix looks like this (assuming you already have a read-request signal declared in your architecture). The following snippet would be inside a Synchronous process:

if (lastState = '0' and newData = '1') then --rising edge of newData
 dataReg(24 downto 0) <= dataResults; -- co-processor results
 dataReg(30 downto 25) <= (others => '0'); -- unused bits
 dataReg(31) <= '1'; -- new data bit
end if;
lastState := newData;
--delay read ACK - you want this to be set AFTER the read has been performed
if (readRq = '1') then
 tempReg <= '1';
end if;
if (tempReg = '1') then
 dataReg(31) <= '0'; -- old data after 1 cycle
 tempReg <= '0';
end if;

Hope you find this neat tip useful!