PDA

View Full Version : Altering a Bi-Directional Data Line


Daragoth
06-16-2004, 07:01 PM
Hello, I'm somewhat of a beginner to the VHDL programming environment
and to electronic design in general and have a question. I have two
devices that communicate with each other using a bi-directional data
line. I have no way of altering these devices, I can only work with
the data line going between them. What I know is that one device,
let's call device X, sends a 32 bit command word every 6 ms to the
other device, let's call device Y. Device Y responds by sending a 86
bit word with data regarding its current state. This entire process
takes about 466 us, which means every bit is active for about 3.95 us
(there is negligible delay between the point where device Y receives
the command and responds).

What I would like to know is how I could intercept the data being sent
from device Y to device X, check to see what it is, and based on this
information, alter it using some code I wrote in VHDL (without
creating any problems with device X, i.e. device X shouldn't "notice"
any change). I know most of the basics of VHDL, but I'm (obviously)
still new to it. I would greatly appreciate any help in the matter.
Thanks.

Sincerely,
Darien A. Gothia

Just an Illusion
06-17-2004, 01:43 PM
Hi Daragoth,

Q. Do you want that you 'interceptor', call it I, appear as Y for X, or
not ?
On paper you can have this situation :

X <==> I <==> Y

Q. Do you have a timing constraint between X request (send) and Y
ackowledge (response) ?
If yes, what is the require minimum reply time for Y ?
The minimum reply time give you your necessary maximum time to make your
alterations, then that give you your frequency.

Based on your description, the communication seems be a serial once, you
need implement two bi-directional input/ouput (inout in vhdl), one for X
(name it p2x) and one for Y (name it p2y).

Inside your I device, the both ports (p2x and p2y) are demuxed according
to expected sens of communication.

Your device I wait some data from X (p2x is 'in', p2y is 'out', and data
are not altered). According to common serial protocol, you have
certainly a sequence to give start of trame, and another to give end of
trame (otherwise you need transmit a synchronization signal); look for
this end trame label, when you capture it, switch the ports
directionnality (p2x is 'out', p2y is 'in').
Now come the alteration circuit, capture returning data in a shift
register (name it bufferYin), frequency ? 1/3.95 10-6 s =~ 253 kHz,
extract shift register content in parallel for your treatment. Do any
alteration and put result into an other shift register (name it bufferYout).
And send the content of bufferYout to X, frequency ? same than bufferYin.
When transmission is ended, you can go back to your initial situation.

All this different steps can be control by a small FSM
(RECEIVEX,RECEIVEY,ALTERATION,SENDX)

According to your given timing you have (466-3.95*86 =) 126.30 us (at
most) to all the process.

Have fun ;-)
JaI

Daragoth wrote:

>Hello, I'm somewhat of a beginner to the VHDL programming environment
>and to electronic design in general and have a question. I have two
>devices that communicate with each other using a bi-directional data
>line. I have no way of altering these devices, I can only work with
>the data line going between them. What I know is that one device,
>let's call device X, sends a 32 bit command word every 6 ms to the
>other device, let's call device Y. Device Y responds by sending a 86
>bit word with data regarding its current state. This entire process
>takes about 466 us, which means every bit is active for about 3.95 us
>(there is negligible delay between the point where device Y receives
>the command and responds).
>
>What I would like to know is how I could intercept the data being sent
>from device Y to device X, check to see what it is, and based on this
>information, alter it using some code I wrote in VHDL (without
>creating any problems with device X, i.e. device X shouldn't "notice"
>any change). I know most of the basics of VHDL, but I'm (obviously)
>still new to it. I would greatly appreciate any help in the matter.
>Thanks.
>
>Sincerely,
>Darien A. Gothia
>
>

Daragoth
06-18-2004, 12:34 AM
Thanks a lot for the help! I forgot to mention something. Every high
bit is actually low for the first 1 us then the next 3 us is high;
every low bit is low for the first 3 us and high for the last 1 us,
approximately. How would I analyze this data using VHDL?

Because I am working with a very limited amount of space, I would like
to minimize the amount of chips I use to perhaps just a FPGA. Is this
possible?

Just an Illusion
06-18-2004, 10:24 AM
Hi Daragoth,

Let me first summarize the information:
1 - A bit is active during 4 us (in true 3.95 us, according to your
first message)
2 - If the bit is high, during the first us, the line is low, then
change to high for the 3 next us; that give us something like:

High => _---

3 - If the bit is low, during the three us, the ligne is low, then
change to high for the last us; that give us something like:

Low => ___-

That's correct ?

Then the following trame give you:

001101 => ___-___-_---_---___-_---

That's always correct ?

If yes, you have two choices, on receiver side:
- Sample any 1 us (f=1MHz), compare the receive trame, and show if its
High or Low, and fill your register
- Sample any 2 us (f= 500 kHz), and fill your register (recommandary
solution)

For the transmitter side, this is not so simple:
- You can have a transmit register which content all the trame (sampled
at 1us), but that give you a register of 344 (86*4) bits. I thinks that
it's too big.
- You can use reuse the content of the receive buffer, and with a
masking's mechanism generate the require send bit (if you look
carefully, you can see that only value during us 2-3 are different
between High and Low.

I am not sure that both solution give you good result.

If you have access to the bit generation mechanism from device Y, try to
reproduce him, it seems the most simple solution.

Rgrds,
JaI

Daragoth wrote:

>Thanks a lot for the help! I forgot to mention something. Every high
>bit is actually low for the first 1 us then the next 3 us is high;
>every low bit is low for the first 3 us and high for the last 1 us,
>approximately. How would I analyze this data using VHDL?
>
>Because I am working with a very limited amount of space, I would like
>to minimize the amount of chips I use to perhaps just a FPGA. Is this
>possible?
>
>

Just an Illusion
06-30-2004, 10:12 AM
Hi Daragoth,

Sorry for the delay, but I have not time to look your code.

One remark about your code and the shared variables:
* comm_state is use like a fsm state. It is better if you modelize
all your fsm with the same structure.
One process to compute the fsm state, and an other process to
compute the outputs.
A typical structure can be:

signal comm_state : comm_type; -- Your current fsm state
signal nxt_comm_state : comm_type; -- Your next fsm state

...

GenNxtState: process (comm_type,clock_count,bitnum,maxbit)
begin
case comm_type is
when wait_X =>
nxt_comm_state <= receive_X;
when receive_X =>
-- When you are in this state you go nowhere
when receive_Y =>
if (clock_count = 6) then
nxt_comm_state <= send_X;
end if;
when send_X =>
if (clock_count = 4 and bitnum = maxbit) then
nxt_comm_state <= wait_X;
end if;
when others => null; -- or better nxt_comm_state <=
wait_X to go back to a know state
end case;
end process;

GenFSMState : process (clock)
-- You need certainly a reset to ensure that you fsm begin at
wait_X state
begin
if falling_edge(clock) then
comm_state <= nxt_comm_state;
end if;
end process;

GenOutputs : process (comm_state)
begin
case comm_state is
...
end case;
end process;

* clock_count is a counter with multi asynchronous reset.
You can made some specific process which manage the counter:

signal reset_counter : std_logic;

genResetCounter : process (comm_state, clock_count)
-- Becarefull, I think that you expected a synchronous reset for
clock_count=6 and clock_count=4
begin
case comm_state is
when receive_X =>
reset_counter <= '0';
when receive_Y =>
if (falling_edge(data_Y) or clock_count = 6) then
reset_counter <= '0';
else
reset_counter <= '1';
end if;
when send_X =>
if (clock_count = 4) then
reset_counter <= '0';
else
reset_counter <= '1';
end if;
when others =>
reset_counter <= '1';
end case;
end process;

GenCounter : process (clock, reset_counter)
begin
if (reset_counter = '0')
clock_count <= 0;
elsif (falling_edge(clock)) then
clock_count := clock_count + 1;
end if;
end process;

In general, you can't synthesize your current code, because comm_state
appear as drive by two clock signals data_X and clock.
More it missing condition to get out from receive_X state.

You have a wait to partially solve you problem of double clock, you can
modifie in my previous example process by:

GenNxtState: process (comm_type, data_X,clock_count,bitnum,maxbit)
begin
case comm_type is
when wait_X =>
if (falling_edge(data_X)) then
nxt_comm_state <= receive_X;
end if;
when receive_X =>
-- I am not sure that you need this state, expect to
drive an external resynchronization circuit for clock, as dpll
-- In fact you certainly need a state to detect Y arrival.
-- Otherwise you need count the number of bits in X
frame, then you couldn't reset clock_count
when receive_Y =>
if (clock_count = 6) then
nxt_comm_state <= send_X;
end if;
when send_X =>
if (clock_count = 4 and bitnum = maxbit) then
nxt_comm_state <= wait_X;
end if;
when others => null; -- or better nxt_comm_state <=
wait_X to go back to a known state
end case;
end process;

To help you understand, design the sytem with in mind that one process
must handle one signal (only).
After, you can merged the process with similar structure (same
sensitivity list, same control structure).

Remember that register must be drive by only one clock, the design must
be locally synchronous, even if it's appear asynchronous at top level.

In any case if you want use a fpga as target, remember that fpga have
generally one clock input (a few with 2) that drive all register cells
(always on the same edge).

I hope that can help you,
JaI

Daragoth wrote:

>Thanks for the tips. What I'm thinking of doing is using a 1 MHz
>clock to sample for the value of a bit 2 us after a falling edge in
>the data line. The only time the data falls is at the begining of a
>new bit. So I would synchronize the clock with each of these edges
>and use a counter to determine how many ticks occur. To find the end
>of a word I would just check if the clock surpasses 5 ticks after
>every synchronization (X sends Y a constant high signal when it is not
>sending data). For sending data I would use the clock, by first
>sending '0', then for the next 2 us the bit's value, then '1', then
>moving to the next bit. Is this a good solution, or is there a better
>way? As I said I want to minimize the number of chips I use as I have
>very limited space so I can't use a seperate chip as a shift register.
>
>Here is the code I made for this process. I would like to change the
>shared variables it uses to signals, but because they are used in
>multiple processes it causes errors. Any idea how to change this?
>Any improvements or suggestions to the method I used?
>
>
>
>library IEEE;
>use IEEE.STD_LOGIC_1164.ALL;
>
>entity mod_X_Y is
> generic (tpd : Time := 10 ns);
> port (clock : in std_logic;
> reset : out std_logic;
> data_Y, data_X : inout std_logic);
>end mod_X_Y;
>
>architecture behavior of mod_X_Y is
> type comm_type is (wait_X, receive_X, receive_Y, send_X);
> shared variable comm_state : comm_type := wait_X;
> shared variable clock_count : integer range 0 to 6;
>begin
> comm_X: process (data_X)
> begin
> if falling_edge(data_X) then
> case comm_state is
> when receive_X =>
> clock_count := 0;
> reset <= '1' after tpd;
> when wait_X =>
> comm_state := receive_X;
> when others => null;
> end case;
> else null;
> end if;
> end process comm_X;
> comm_Y: process (data_Y)
> begin
> if falling_edge(data_Y) then
> case comm_state is
> when receive_Y =>
> clock_count := 0;
> reset <= '1' after tpd;
> when others => null;
> end case;
> else null;
> end if;
> end process comm_Y;
> count_up: process (clock)
> -- insert constants and variables pertaining to modification
>here
> constant word_len_Y : natural := 86;
> variable data_i, data_o, data_q : std_logic_vector(word_len_Y -
>1 downto 0);
> variable bitnum, maxbit : integer range 0 to word_len_Y - 1 :=
>0;
> begin
> if falling_edge(clock) then
> clock_count := clock_count + 1;
> case comm_state is
> when send_X =>
> data_Y <= '1' after tpd;
> case clock_count is
> when 1 =>
> data_X <= '0' after tpd;
> when 2 | 3 =>
> data_X <= data_o(bitnum) after tpd;
> when 4 =>
> data_X <= '1' after tpd;
> clock_count := 0;
> bitnum := bitnum + 1;
> if bitnum = maxbit then
> comm_state := wait_X;
> bitnum := 0;
> else null;
> end if;
> when others => null;
> end case;
> when others =>
> data_Y <= data_X after tpd;
> data_X <= '1' after tpd;
> end case;
> if comm_state = receive_Y then
> case clock_count is
> when 3 =>
> data_i(bitnum) := data_Y;
> bitnum := bitnum + 1;
> when 6 =>
> comm_state := send_X;
> data_o := data_i;
> -- insert modification code here
> data_q := data_i;
> clock_count := 0;
> maxbit := bitnum;
> bitnum := 0;
> when others => null;
> end case;
> else null;
> end if;
> else null;
> end if;
> end process count_up;
>end behavior;
>
>
>
>If the code is difficult to read, I made a commented version of it
>too:
>
>
>
>library IEEE;
>use IEEE.STD_LOGIC_1164.ALL;
>
>entity mod_X_Y is
> generic (tpd : Time := 10 ns);
> port (clock : in std_logic;
>-- 1 MHz clock
> reset : out std_logic;
>-- resets clock
> data_Y, data_X : inout std_logic);
>-- communication lines to devices X and Y
>end mod_X_Y;
>
>architecture behavior of mod_X_Y is
> type comm_type is (wait_X, receive_X, receive_Y, send_X);
> shared variable comm_state : comm_type := wait_X;
> shared variable clock_count : integer range 0 to 6;
>begin
> comm_X: process (data_X)
> begin
> if falling_edge(data_X) then
> case comm_state is
> when receive_X =>
> clock_count := 0;
>-- synchronizes clock with data stream
> reset <= '1' after tpd;
> when wait_X =>
> comm_state := receive_X;
>-- 'wakes' device out of waiting state
> when others => null;
> end case;
> else null;
> end if;
> end process comm_X;
> comm_Y: process (data_Y)
> begin
> if falling_edge(data_Y) then
> case comm_state is
> when receive_Y =>
> clock_count := 0;
>-- synchronizes clock with data stream
> reset <= '1' after tpd;
> when others => null;
> end case;
> else null;
> end if;
> end process comm_Y;
> count_up: process (clock)
> -- insert constants and variables pertaining to modification here
> constant word_len_Y : natural := 86;
> variable data_i, data_o, data_q : std_logic_vector(word_len_Y - 1
>downto 0);
> variable bitnum, maxbit : integer range 0 to word_len_Y - 1 := 0;
> begin
> if falling_edge(clock) then
> clock_count := clock_count + 1;
>-- increment clock counter every time clock ticks
> case comm_state is
> when send_X =>
> data_Y <= '1' after tpd;
>-- send high bits to Y when communicating with X
> case clock_count is
> when 1 =>
> data_X <= '0' after tpd;
>-- 1st us of the bit is always low
> when 2 | 3 =>
> data_X <= data_o(bitnum) after tpd;
>-- 2nd and 3rd us of the bit are equal to the bit's value
> when 4 =>
> data_X <= '1' after tpd;
>-- 4th bit is always high
> clock_count := 0;
>-- reset clock counter
> bitnum := bitnum + 1;
>-- begin process again on next bit
> if bitnum = maxbit then
> comm_state := wait_X;
> bitnum := 0;
>-- if the last bit of the word has been sent, go
>-- back to initial conditions and wait for X
> else null;
> end if;
> when others => null;
> end case;
> when others =>
> data_Y <= data_X after tpd;
>-- send to Y what X is sending if not in the process of sending to X
> data_X <= '1' after tpd;
>-- only time Y doesn't send high bits to X is taken care of already
> end case;
> if comm_state = receive_Y then
> case clock_count is
> when 3 =>
> data_i(bitnum) := data_Y;
> bitnum := bitnum + 1;
>-- when 2 us have passed after the falling edge of data,
>-- sample data for its value, then increment bit
> when 6 =>
>-- because of the reset and synchronization, the clock should
>-- never get this far unless the word of data has been finished
> comm_state := send_X;
> data_o := data_i;
>-- for use with modification
> -- insert modification code here
> data_q := data_i;
>-- for use with modification
> clock_count := 0;
> maxbit := bitnum;
>-- if receiving data from Y, record the length of the word
> bitnum := 0;
>-- prepare for sending word
> when others => null;
> end case;
> else null;
> end if;
> else null;
> end if;
> end process count_up;
>end behavior;
>
>
>
>Thanks a lot for any help regarding the issue.
>
>