Hey I'm a beginner when it comes to VHDL and currently I'm trying to write a protocol decoder for the open pixel control (OPC) protocol: http://openpixelcontrol.org/
I have implemented a FSM in VHDL to decode the OPC messages by reading bytes from a serial/RS232 receiver. The RX module outputs the received byte as a std_logic_vector and creates a data ready pulse that is one gclk cycle long.
In my top level entity I have connected the RX and the decoder modules to the same global clock net. The data ready pulse from the RX module is directly connected to the i_dataready input of the FSM in question.
Although I have checked the code for this FSM multiple times in the last couple of days and also searching a fair bit I couldn't find out why my FSM won't change states.
The thing that puzzles me is that two other modules, a serial TX and another module change states just fine upon receiving the data ready signal from the RX module, it's just this FSM that doesn't work.
This is the code that I have written:
EDIT: okay I've done some more simulation of my complete top level entity: The system is currently running at 2MHz (I know very slow but it's all I can do right now).
According to simulation in Active-HDL the system should work as expected.
Here is my top level entity:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity serial_test is
port(
i_gclk: in std_logic;
i_rx: in std_logic;
o_tx: out std_logic;
o_go: out std_logic;
o_newdata: out std_logic;
o_ws: out std_logic;
o_leds: out std_logic_vector(2 downto 0)
);
end serial_test;
architecture rtl of serial_test is
signal txact, txdone: std_logic;
signal rxdata: std_logic_vector(7 downto 0);
signal rgbdata: std_logic_vector(7 downto 0);
signal opc_chan: std_logic_vector(7 downto 0);
signal go: std_logic;
signal opc_state: std_logic_vector(2 downto 0);
signal newrgb: std_logic;
begin
opcdec_inst: entity work.opc_decoder
port map(i_gclk => i_gclk, i_dataready => go, i_rawdata => rxdata, o_channel => opc_chan, o_rgbdata => rgbdata, o_state => opc_state, o_newdata => newrgb);
urx_inst: entity work.uart_rx
port map(i_clk => i_gclk, i_rx_serial => i_rx, o_rx_dv => go, o_rx_byte => rxdata);
utx_inst: entity work.uart_tx
port map(i_clk => i_gclk, i_tx_dv => go, i_tx_byte => rxdata, o_tx_active => txact, o_tx_serial => o_tx, o_tx_done => txdone);
wsdrv_inst: entity work.ws2812_driver
port map(i_gclk => i_gclk, i_rgbdata => rgbdata, i_newdata => newrgb, o_serial => o_ws);
-- output FSM state to LEDs
o_leds <= opc_state;
-- output go signal for signal analyzer
o_go <= go;
o_newdata <= newrgb;
end rtl;
My testbench for the top level entity
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
ENTITY testbench IS
END testbench;
ARCHITECTURE behavior OF testbench IS
COMPONENT serial_test
PORT(
i_gclk : IN std_logic;
i_rx : IN std_logic;
o_tx: OUT std_logic;
o_go : OUT std_logic;
o_newdata : OUT std_logic;
o_ws : OUT std_logic;
o_leds : OUT std_logic_vector(2 downto 0)
);
END COMPONENT;
SIGNAL i_gclk : std_logic := '1';
SIGNAL i_rx : std_logic := '1';
SIGNAL o_tx : std_logic;
SIGNAL o_go : std_logic;
SIGNAL o_newdata : std_logic;
SIGNAL o_ws : std_logic;
SIGNAL o_leds : std_logic_vector(2 downto 0);
BEGIN
-- Please check and add your generic clause manually
uut: serial_test PORT MAP(
i_gclk => i_gclk,
i_rx => i_rx,
o_tx => o_tx,
o_go => o_go,
o_newdata => o_newdata,
o_ws => o_ws,
o_leds => o_leds
);
p_GCLK: process
begin
i_gclk <= not i_gclk;
wait for 250ns;
end process p_GCLK;
p_RX: process
begin
--wait for 375ns;
wait for 1ms;
-- first byte: 0x01 (CHANNEL)
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
-- second byte: 0x00 (COMMAND)
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
-- third byte 0x00(LENGTH MSB)
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
-- fourth byte 0x03 (LENGTH LSB)
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
-- fifth byte 0xCD
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
-- sixth byte 0xBA
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
-- seventh byte 0xFE
i_rx <= '0';
wait for 104us;
i_rx <= '0';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
i_rx <= '1';
wait for 104us;
wait for 10ms;
end process p_RX;
END;
Here you can see the waveforms that the simulator produces. It clearly shows that at least in simulation the FSM does indeed change states and even o_newdata works as expected. eliaselectronics.com/wp-content/uploads/2014/08/opc_dec_simulation.jpg (sorry I can't yet post images on StackOverflow, uploaded them to my webiste instead)

This screenshot shows the signals from the Lattice CPLD that were actually measured with the logic analyzer. In this case we can see that there is no change in states, o_ws and o_newdata don't change either. eliaselectronics.com/wp-content/uploads/2014/08/opc_dec_measured.png

Now knowing that my FSM code should be okay, I presume this has something to do with the actual synthesis into hardware, maybe things like excessive propagation delay or clock skew. Is there a way to get around this in some way?
This is the FSM code in question
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
entity opc_decoder is
port(
i_gclk: in std_logic; -- global system clock
i_dataready: in std_logic; -- data ready signal from previous block (length: 1 gclk cycle)
i_rawdata: in std_logic_vector(7 downto 0); -- raw byte received by previous block
o_channel: out std_logic_vector(7 downto 0); -- output channel address for output MUX
o_rgbdata: out std_logic_vector(7 downto 0); -- decoded OPC data byte (R|G|B)
o_state: out std_logic_vector(2 downto 0);
o_newdata: out std_logic -- data synchronization signal for next block
);
end opc_decoder;
architecture rtl of opc_decoder is
type t_OPC_SM is (s_IDLE, s_OPC_CMD, s_OPC_LEN_HI, s_OPC_LEN_LO, s_OPC_DATA);
signal r_OPC_SM: t_OPC_SM := s_IDLE;
signal r_OPC_CHANNEL: std_logic_vector(7 downto 0); -- decoded channel number/address
signal r_OPC_COUNT: unsigned(15 downto 0); -- number of actual RGB data bytes to be received
begin
p_OPC_SM: process(i_gclk) is
begin
if(rising_edge(i_gclk)) then
o_newdata <= '0';
case r_OPC_SM is
when s_IDLE =>
r_OPC_CHANNEL <= (others=>'0');
r_OPC_COUNT <= (others=>'0');
if(i_dataready = '1') then
r_OPC_CHANNEL <= i_rawdata;
r_OPC_SM <= s_OPC_CMD;
else
r_OPC_SM <= s_IDLE;
end if;
when s_OPC_CMD =>
if(i_dataready = '1') then
r_OPC_SM <= s_OPC_LEN_HI;
else
r_OPC_SM <= s_OPC_CMD;
end if;
when s_OPC_LEN_HI =>
if(i_dataready = '1') then
r_OPC_COUNT <= unsigned(i_rawdata) & r_OPC_COUNT(7 downto 0);
r_OPC_SM <= s_OPC_LEN_LO;
else
r_OPC_SM <= s_OPC_LEN_HI;
end if;
when s_OPC_LEN_LO =>
if(i_dataready = '1') then
r_OPC_COUNT <= r_OPC_COUNT(15 downto 8) & unsigned(i_rawdata);
r_OPC_SM <= s_OPC_DATA;
else
r_OPC_SM <= s_OPC_LEN_LO;
end if;
when s_OPC_DATA =>
if(i_dataready = '1') then
if (r_OPC_COUNT > 0) then
o_rgbdata <= i_rawdata;
o_newdata <= '1';
r_OPC_COUNT <= r_OPC_COUNT - 1;
r_OPC_SM <= s_OPC_DATA;
else
r_OPC_SM <= s_IDLE;
end if;
else
r_OPC_SM <= s_OPC_DATA;
end if;
when others =>
r_OPC_SM <= s_IDLE;
end case;
end if;
end process p_OPC_SM;
-- output state information
with r_OPC_SM select
o_state <= "001" when s_IDLE,
"010" when s_OPC_CMD,
"011" when s_OPC_LEN_HI,
"100" when s_OPC_LEN_LO,
"101" when s_OPC_DATA,
"000" when others;
o_channel <= r_OPC_CHANNEL;
end rtl;
Thanks a lot, Elia

