Coding is all about trial and errors and learning from the errors. My independent study project deals with a controller for 8080-type parallel interface. The interface protocol is:
Kudos to my friends who helped me debug the earlier versions of this code.
Take away 1: Write only the inputs in the sensitivity list, this code worked when signals arent included in the sensitivity list.
2: If the code stays in one state over multiple clock cycles, clock is to be included in the sensitivity list.
3. Once you include clock in the sensitivity list, signals are updated at each clock transition. This may lead to erroneous results.
 |
| signal was getting updated at every clock edge instead of just the positive edge |
Solution: use next_<signal name> which will be updated only at the required clock edge.
4. Biggest takeaway, simple subtraction of vectors works too, no need of converting to unsigned/ signed and then reconversion back to std_logic_vector. :)
5. Initial red signal for sact, where i was subtracting en_active - dsu. Not to worry, it simply means inputs not present yet.
--------------------------------------------------------------------------------------------------
asu= address setup time, between assertion of RS and when CSb=0
A cycle delay between CSb asserted and enable asserted.
dsu= data setup time, time at which stable data is put on bus (dbus <= data_in )
en_active = # of clock cycles for which enable is heldlow
ahold = address hold time, time between end of en_active period and when RS is deaaerted.
dhold = data hold time, time for which data is held on bus after end of en_active period.
en_deactive= # clock cycles for which enable is held high.
-------------------------------------------------------------------------------------------------
-- if reset : s0
--if ready: s1 --pull up RS
--if asu counter=0 : s1sub0 pull CSb
--s2: put en
--en_active - dsu counter: s3: put data on bus
-- dsu counter up: s3sub0 : pull off enable, CSb
-- dhold counter: s4: pull off data; -- ahold counter: pull off RS -- deactive counter up: restart the sequence
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.std_logic_unsigned.all;
entity fsm_8080 is
Port ( clk, rst, ready, rs_in : in
std_logic;
asu, dsu, ahold, dhold : in
STD_LOGIC_VECTOR (1 downto 0);
en_active, en_deactive: in
STD_LOGIC_VECTOR (3 downto 0);
data_in : in
STD_LOGIC_VECTOR (15 downto 0);
dbus : out
STD_LOGIC_VECTOR (15 downto 0);
RS, CSb, en : out
STD_LOGIC);
end fsm_8080;
architecture Behavioral of fsm_8080 is
type STATE_TYPE is (s0, s1, s1sub0, s2, s3, s3sub0, s4);
signal state, next_state : STATE_TYPE;
signal sact, next_sact, sdeact, next_sdeact:
STD_LOGIC_VECTOR (3 downto 0);
signal sasu, next_sasu, sdsu, next_sdsu, sahold, next_sahold, sdhold, next_sdhold:
STD_LOGIC_VECTOR (1 downto 0);
begin
process(clk, rst)
begin
if (rst = '1') then
state <= s0;
sact <= en_active - dsu;
sdeact <= en_deactive;
sdhold <= dhold;
sahold <= ahold;
sdsu <= dsu;
sasu <= asu;
elsif(clk'event and clk = '1') then
state <= next_state;
sact <= next_sact;
sdeact <= next_sdeact;
sasu <= next_sasu;
sdsu <= next_sdsu;
sahold <= next_sahold;
sdhold <= next_sdhold;
end if;
end process;
process(clk, ready, rs_in, state, data_in, asu, dsu, dhold, ahold, en_active, en_deactive)
begin
next_state <= state;
--assume this unless changed later, simplifies logic
next_sact <= sact;
next_sdeact <= sdeact;
next_sasu <= sasu;
next_sdsu <= sdsu;
next_sahold <= sahold;
next_sdhold <= sdhold;
case state is
when s0 => en <= '1'; RS <= '-'; CSb <= '1'; dbus <= (others => '-');
next_sact <= en_active - dsu -1;
next_sdeact <= en_deactive;
next_sdhold <= dhold;
next_sahold <= ahold;
next_sdsu <= dsu;
next_sasu <= asu;
if (ready='1') then
next_state <= s1;
end if;
when s1 => rs <= rs_in; en <='1'; dbus <= (others => '-');
--assert RS
if (asu="00") then CSb <= '0'; next_state <= s2;
else CSb <= '1'; next_state <= s1sub0;
end if;
when s1sub0 => rs <= rs_in; en <='1'; dbus <= (others => '-');
-- CSb goes low
next_sasu <= sasu - "01";
if next_sasu="00" then next_state <= s2; CSb <= '0';
else CSb <= '1';
end if;
when s2 => rs <= rs_in; en <='0'; CSb <= '0'; dbus <= (others => '-');
--enable goes low
next_sact <= sact - "0001";
if next_sact="0000" then
next_state <= s3;
end if;
when s3 => rs <= rs_in; en <='0'; CSb <= '0'; dbus <= data_in;
--put data on bus. Effective dsu=dsu+1 so that we dont mess up if user enters dsu and dhold both 0.
if (dsu="00") then next_state <= s4;
else next_state <= s3sub0;
end if;
when s3sub0 => rs <= rs_in; dbus<= data_in;
--end of enable_active, pull up en and CSb
next_sdsu <= sdsu - "01";
if next_sdsu="00" then next_state <= s4; en <='1'; CSb <= '1';
else en <= '0'; CSb <= '0';
end if;
when s4 => en <='1'; CSb <= '1';
--enable deactive
if sdhold="00" then dbus <= (others => '-');
else
next_sdhold <= sdhold - "01";
dbus <= data_in;
end if;
if sahold="00" then rs <= '-';
else
next_sahold <= sahold - "01";
rs <= rs_in;
end if;
next_sdeact <= sdeact - "0001";
if next_sdeact="0000" then
next_state <= s0;
--sequence starts again
else
next_state <= s4;
end if;
end case;
end process;
end Behavioral;
End result: Protocol followed- check.
Signals updated at the correct edge - check.
Post script: Feels great when you contribute your code on the net. You feel better about googling for the next part of the project :)
Post script 2: The same controller implemented in Verilog: my 2nd code in Verilog
8080-type parallel interface controller in Verilog
https://docs.google.com/document/d/15n1rD3f3zQXb7LQdjMZ6EaEGnNmj1GBhOC-lWoBw4Vw/edit
Could have used 3 bits to encode states. HDL compiler used one-hot encoding.
Added default state= s0.
Note always @ (posedge clk) for the 2nd always, no need of a huge sensitivity list.
This one used 2 additional things..done output to indicate that end of sequence, signal s_data_in to latch input data.
Output bus is pulled to high impedance state (by 16'bz) when data is supposedly safely latched by the reader chip.