I'm implementing a simple SPI master in VHDL. The problem I'm facing is that during the synthesising two flip flops are created for ss
and data_rdy
. I thought that the output to those ports is always specified, so why are these registers created, and how can I get rid of them?
My code is below, with the states that don't have ss
or data_rdy
in them omitted.
entity:
library IEEE;
use IEEE.std_logic_1164.ALL;
entity mcu is
port(clk : in std_logic;
res : in std_logic;
pc : in std_logic_vector(7 downto 0);
pc_new : in std_logic;
data_ack : in std_logic;
miso : in std_logic;
data : out std_logic_vector(12 downto 0);
data_rdy : out std_logic;
mosi : out std_logic;
sclk : out std_logic;
ss : out std_logic);
end mcu;
Architecture:
library IEEE;
use IEEE.std_logic_1164.ALL;
architecture behaviour of mcu is
-- r: send read command
-- a: send address
-- rx: receive data
type state_t is (r0, r1, r2, r3, r4, r5, r6, r7,
a0, a1, a2, a3, a4, a5, a6, a7,
rx0, rx1, rx2, rx3, rx4, rx5, rx6, rx7, rx8, rx9, rx10, rx11, rx12,
idle, starting, done);
signal state : state_t := idle;
signal datasig : std_logic_vector(12 downto 0);
begin
sclk <= clk;
mosi <= datasig(12);
sync : process(clk) is
begin
if rising_edge(clk) then
data_rdy <= '0';
ss <= '0';
if res = '1' then
state <= idle;
else
datasig <= datasig(11 downto 0) & miso;
if pc_new = '1' then
state <= starting;
else
case state is
when idle =>
ss <= '1';
datasig <= (others => '0');
state <= idle;
...
when rx12 =>
data <= datasig;
data_rdy <= '1';
state <= done;
when done =>
if data_ack = '1' then
state <= idle;
else
state <= done;
end if;
end case;
end if;
end if;
end if;
end process sync;
end behaviour;
The relevant synthesiser output:
===============================================================================
| Register Name | Type | Width | Bus | MB | AR | AS | SR | SS | ST |
===============================================================================
| data_rdy_reg | Flip-flop | 1 | N | N | N | N | Y | N | N |
| ss_reg | Flip-flop | 1 | N | N | N | N | Y | N | N |
| data_reg | Flip-flop | 13 | Y | N | N | N | N | N | N |
| state_reg | Flip-flop | 3 | Y | N | N | N | N | Y | N |
| state_reg | Flip-flop | 2 | N | N | N | N | Y | Y | N |
| datasig_reg | Flip-flop | 13 | Y | N | N | N | N | N | N |
===============================================================================
Also, why is state
split into two registers?
Registering your outputs is normally a Good Thing : it allows much better defined timings, which can translate to more reliable operation, especially with external devices like SPI peripherals.
The problem (I think) you are facing is that when you want an output to be true whenever you are in a certain state, assigning that output while you are in that state (within the clocked process) will introduce a clock cycle delay.
David Koontz gives one answer : move all the assignments to that output out of the clocked part - or indeed out of that process altogether, dependent only on the "state" signal.
But another way will preserve the register for clean timings, and still eliminate that cycle delay. That is to advance the output assignment to any condition in any state that transitions into that state, or (once in it) doesn't transition out of it. So for example, modify
when rx12 =>
data <= datasig;
data_rdy <= '1';
state <= done;
and the transition into state rx12
when rx11 =>
if ready_to_go_to_rx12 then
data_rdy <= '1'; -- keep data_rdy synched with rx12
state <= rx12;
end if;
when rx12 =>
data <= datasig;
-- data_rdy <= '1'; -- no, because we leave state rx12 immediately
state <= done;
and likewise for other outputs with the same problem.
In all probability this doesn't create extra logic, because the synth tool can usually identify that data_rdy
and rx12
are always asserted at the same time, and share a register for them.
EDIT : I came back for another look, and noticed that data <= datasig;
is also registered, and therefore also a cycle late : while I only considered data_rdy
as one of the signals you mentioned, you need to consider if other assignments like data <= datasig;
should also be advanced. (I would guess so : it's counterintuitive to signal data_rdy
in the cycle before new data!)