-1
\$\begingroup\$

I am trying to create a FSM that gets a 128-bit-long serial input and outputs it in a parallel way. The final goal is to synthesise the code for an ASIC implementation using Cadence environment. Meanwhile I'm working in Vivado.

I have a doubt on how to handle correctly the signals. For example, to avoid hold and setup times problems, I register the FSM states and signals on the falling edge of the clock, while my signals and outputs arrive and get registered on the rising edge. Is it correct to do it like I did, or is there a better way of handling this stuff?

Here there is a schematic flow diagram of my FSM: FSM diagram

I want to perform a sort of integrity check, by receiving a head and a tail 8-bit signals, and comparing them to some hardcoded values. To do so, I introduce two states: RECEIVE_HEAD and RECEIVE_TAIL, in between of RECEIVE_PACKET, and after I received everything I do the comparison in CHECK_HEAD and CHECK_TAIL.

The problem is that, from what I see, for each packet I need a different counter, otherwise when I am receiving the next data (for example TAIL), the HEAD packet also changes in value. Is there a way to change each packet only when needed, without creating many separate equal counters?

Also, when I finish receiving the word in RECEIVE_PACKET, I want it not to change its value anymore, but at the beginning of the process I update all the counters to be (others => '1'), so in the next clock cycle the value of packet("1111111") changes.

For sure there is something wrong with the architecture I have designed. How can I make it more robust and working? Is there any literature I can look to?

library IEEE,STD;
use IEEE.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity WL_dec is
    port(
        wl_out      :   OUT std_logic_vector(127 downto 0);
        data_out    :   OUT std_logic;
        enable      :   IN  std_logic;
        data_in     :   IN  std_logic;
        reset       :   IN  std_logic;
        clock       :   IN  std_logic
    );
end WL_dec;

architecture Behavioral of WL_dec is
    type    state_type  is  (IDLE, RECEIVE_HEAD, RECEIVE_PACKET, RECEIVE_TAIL, CHECK_HEAD, CHECK_TAIL, WRONG, ECHO, INSTR_EXE, FIRE);
    
    signal  head_packet, tail_packet                :   std_logic_vector(7 downto 0);
    signal  packet                                  :   std_logic_vector(127 downto 0);
    signal  head_packet_next,
            packet_next, tail_packet_next, 
            data_out_next, data_in_reg              :   std_logic;
    signal  ss_current, ss_next                     :   state_type;

    signal  count_bits_head, count_bits_head_next,
            count_bits_tail, count_bits_tail_next,
            count_wrong, count_wrong_next,
            count_echo, count_echo_next             :   std_logic_vector(2 downto 0);

    signal  count_word, count_word_next             :   std_logic_vector(6 downto 0);
    
    constant    ack     :   std_logic_vector(7 downto 0)    := "10011001";
    constant    nack    :   std_logic_vector(7 downto 0)    := "10110001";


begin

    process(clock,reset)
    begin
        if (reset = '1') then
            ss_current        <= IDLE;
            data_in_reg       <= '0';
            count_bits_head   <= (others => '1');
            count_bits_tail   <= (others => '1');
            count_word        <= (others => '1');
            count_echo        <= (others => '1');
            count_wrong       <= (others => '1');
        elsif (falling_edge(clock)) then
            ss_current        <= ss_next;
            data_in_reg       <= data_in;
            count_bits_head   <= count_bits_head_next;
            count_bits_tail   <= count_bits_tail_next;
            count_word        <= count_word_next;
            count_echo        <= count_echo_next;
            count_wrong       <= count_wrong_next;
        end if;
    end process;

    process(clock, reset)
    begin
        if (reset = '1') then
            head_packet   <=  (others => '0');
            tail_packet   <=  (others => '0');
            packet        <=  (others => '0');
            data_out        <=  '0';
        elsif (rising_edge(clock)) then
            head_packet(to_integer(unsigned(count_bits_head)))  <= head_packet_next;
            tail_packet(to_integer(unsigned(count_bits_tail)))  <= tail_packet_next;
            packet(to_integer(unsigned(count_word)))            <= packet_next;
            data_out                                            <= data_out_next;
        end if;
    end process;

    FSM :   process(all)
    begin
        data_out_next           <= '0';
        WL_out                  <= (others => '0');
        count_bits_head_next    <= (others => '1');
        count_bits_tail_next    <= (others => '1');
        count_word_next         <= (others => '1');
        count_echo_next         <= (others => '1');
        count_wrong_next        <= (others => '1');

        case ss_current is
            when IDLE   =>
                if (data_in_reg = '1') then
                    ss_next                   <= RECEIVE_HEAD;
                    head_packet_next          <= data_in_reg;
                    count_bits_head_next      <= count_bits_head-1;
                else
                    ss_next <= IDLE;
                end if;
            
            when RECEIVE_HEAD =>
                if (count_bits_head = 0) then
                    head_packet_next      <= data_in_reg;
                    count_bits_head_next  <= count_bits_head;
                    ss_next               <= RECEIVE_PACKET;
                else
                    head_packet_next      <= data_in_reg;
                    count_bits_head_next  <= count_bits_head-1;
                    ss_next               <= RECEIVE_HEAD;
                end if; 
            when RECEIVE_PACKET =>
                if (count_word = 0) then
                    packet_next           <= data_in_reg;
                    count_word_next       <= count_word;
                    ss_next               <= RECEIVE_TAIL;
                else
                    packet_next           <= data_in_reg;
                    count_word_next       <= count_word-1;
                    ss_next               <= RECEIVE_PACKET;
                end if; 
            when RECEIVE_TAIL =>
                if (count_bits_tail = 0) then
                    tail_packet_next      <= data_in_reg;
                    count_bits_tail_next  <= count_bits_tail;
                    ss_next               <= CHECK_HEAD;
                else
                    tail_packet_next      <= data_in_reg;
                    count_bits_tail_next  <= count_bits_tail-1;
                    ss_next               <= RECEIVE_TAIL;
                end if;
            when CHECK_HEAD   =>
                if (head_packet = "10101101") then
                    ss_next <= CHECK_TAIL;
                else
                    ss_next <= WRONG;
                end if;
            when CHECK_TAIL   =>
                if (tail_packet = "10110011") then
                    ss_next <= ECHO;
                else
                    ss_next <= WRONG;
                end if;
            when WRONG   =>
                if (count_wrong = 0) then
                    count_wrong_next    <= count_wrong;
                    data_out_next       <= nack(to_integer(unsigned(count_wrong)));
                    ss_next             <= IDLE;
                else
                    count_wrong_next    <= count_wrong-1;
                    data_out_next       <= nack(to_integer(unsigned(count_wrong)));
                    ss_next             <= WRONG;
                end if;   
            when ECHO   =>
                if (count_echo = 0) then
                    count_echo_next   <= count_echo;
                    data_out_next     <= ack(to_integer(unsigned(count_echo)));
                    ss_next           <= INSTR_EXE;
                else
                    count_echo_next   <= count_echo-1;
                    data_out_next     <= ack(to_integer(unsigned(count_echo)));
                    ss_next           <= ECHO;
                end if;   
            when INSTR_EXE   =>
                if (enable = '1') then
                    ss_next <= FIRE;
                else
                    ss_next <= INSTR_EXE;
                end if;
            when    FIRE     =>
                WL_out  <= packet;
                if (enable = '0') then
                    ss_next   <= IDLE;
                else
                    ss_next   <= FIRE;
                end if;
        when others =>
            ss_next     <= IDLE;
        end case;
    end process;
end Behavioral;
\$\endgroup\$
8
  • 3
    \$\begingroup\$ I think you should work with a single input shift register, and whenever you have received enough bits move it to the corresponding location (e.g. your head_packet). You could probably even omit the extra head and tail packets and check the values directly once all bits have been received, also saving two separate states. \$\endgroup\$
    – po.pe
    Aug 2 at 10:35
  • 6
    \$\begingroup\$ I am not sure why you think capturing on both edges relaxes setup/hold timing, in fact it has tightened the setup timing because STA tool now has to meet timing on bunch of half-cycle paths which you have created. \$\endgroup\$
    – Mitu Raj
    Aug 2 at 10:45
  • 1
    \$\begingroup\$ I am not sure of the synchronicity of "data_in" with "clock"." -- But it's your job as designer to be absolutely sure of how's the timing of signals i.e., the specifications of input signal (For eg: Is it a source synchronous signal? Or asynchronous signal? etc...). Only then, you can start off designing the input interface. \$\endgroup\$
    – Mitu Raj
    Aug 2 at 14:56
  • 1
    \$\begingroup\$ If data and clock are coming aligned, you can use the same clock to register the data within the RTL logic. You have to phase shift the clock by half cycle and center-align the data for this purpose. This assumes that the jitter/skew are minimal and limited within half clock cycle, and signal integrity is taken care. \$\endgroup\$
    – Mitu Raj
    Aug 2 at 15:37
  • 1
    \$\begingroup\$ Looking at other protocol can give insights in such considerations of clocks and timings. You might like to start with SPI. \$\endgroup\$ Aug 3 at 5:47

0

Your Answer

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

Browse other questions tagged or ask your own question.