Altering a Bi-Directional Data Line

D

Daragoth

Guest
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
 
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
 
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?
 
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?
 
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.
 
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.
 

Welcome to EDABoard.com

Sponsor

Back
Top