Skip to content

Commit

Permalink
Work on fifo and gearbox, maybe still bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
unhold committed Jul 25, 2018
1 parent 037bc9b commit 3136a89
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 43 deletions.
16 changes: 16 additions & 0 deletions eda/compile.do
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
vcom ../vhdl/rtl_pack.vhd
vcom ../vhdl/tb_pack.vhd
vcom ../vhdl/state_pack.vhd
vcom ../vhdl/greycode_pack.vhd
vcom ../vhdl/clk_div.vhd
vcom ../vhdl/pulse_gen.vhd
vcom ../vhdl/stb_gen.vhd
vcom ../vhdl/sync.vhd
vcom ../vhdl/pll.vhd
vcom ../vhdl/dual_port_ram.vhd
vcom ../vhdl/fifo.vhd
vcom ../vhdl/gearbox.vhd
vcom ../vhdl/gearbox_tb.vhd
vcom ../vhdl/codec_8b10b_pack.vhd
vcom ../vhdl/data_8b10b_tb.vhd
vcom ../vhdl/pcs_6466b.vhd
141 changes: 141 additions & 0 deletions vhdl/example/safe_state_machine.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
library ieee;
use ieee.std_logic_1164.all;

library work;
use work.state_pack.all;

-- Example for a unit implementing a safe state machine using state_pack.
entity safe_state_machine is
generic (
encoding : encoding_t);
port (
clock_i : in std_logic;
reset_i : in std_logic := '0'; -- Asynchronous reset, default value indicates that this unit can work without initial reset, for FPGA designs.
enable_i : in std_logic := '1'; -- Clock enable, may be used to divide the clock.
error_o : out std_logic;
state_inject_i : in inject_t := inject_off_c);
end;

architecture rtl of safe_state_machine is

type my_enum_t is (a, b, c);
constant my_enum_len_c : natural := my_enum_t'pos(my_enum_t'right) + 1;
subtype my_enum_code_t is code_t(code_length(my_enum_len_c, encoding)-1 downto 0);

type state_t is record
my_enum_code : my_enum_code_t;
end record;

constant reset_state_c : state_t := (
my_enum_code => encode(my_enum_t'pos(a), my_enum_len_c, encoding));

-- If your implementation tool is smart enough to for sequential optimizations,
-- you have to set the appropirate attribute to avoid recoding of the state!
-- This may be a VHDL attribute, a TCL command or another tool-specific setting.
-- Using the state_inject_i input and making it externally controllabe
-- will also avoid optimizations and preserve the state encoding.
signal state, next_state : state_t := reset_state_c;
-- Initial value required for FPGA designs without reset.

-- Disable sequential optimizations for Synopsys and Synplicity.
attribute syn_preserve : boolean;
attribute syn_preserve of state : signal is true;

begin

process(clock_i, reset_i)
begin
if reset_i = '1' then
state <= reset_state_c;
elsif rising_edge(clock_i) then
if enable_i = '1' then
state <= next_state;
end if;
end if;
end process;

process(state, state_inject_i)
variable my_enum_v, next_my_enum_v : my_enum_t;
begin

error_o <= '0';
if error(state.my_enum_code, encoding) then
-- Handle uncorrectable errors.
-- Optional, as only some encodings can have errors that are detectable but not correctable.
my_enum_v := a;
error_o <= '1';
else
-- Decode.
my_enum_v := my_enum_t'val(decode(state.my_enum_code, encoding));
end if;

-- State machine logic.
case my_enum_v is
when a => next_my_enum_v := b;
when b => next_my_enum_v := c;
when c => next_my_enum_v := a;
end case;

-- Encode and handle state injection.
next_state <= (
my_enum_code => handle_inject(
encode(my_enum_t'pos(next_my_enum_v), my_enum_len_c, encoding),
state_inject_i));
end process;

end;


entity safe_state_machine_tb is
end;

library ieee;
use ieee.std_logic_1164.all;

library work;
use work.state_pack.all;
use work.tb_pack.all;

architecture tb of safe_state_machine_tb is

signal run : boolean := true;
signal clock_i : std_ulogic;
signal state_inject_i : inject_t;

begin

clk_gen(clock_i, run);

ssme_gen : for encoding in encoding_t generate
ssme : entity work.safe_state_machine
generic map (
encoding => encoding)
port map (
clock_i => clock_i,
state_inject_i => state_inject_i);
end generate;

process
begin
wait_clk(clock_i, 5);

state_inject_i <= (
index => 1,
write => true);
wait_clk(clock_i, 1);
state_inject_i <= inject_off_c;
wait_clk(clock_i, 1);

state_inject_i <= (
index => 1,
write => true);
wait_clk(clock_i, 1);
state_inject_i <= inject_off_c;
wait_clk(clock_i, 1);

wait_clk(clock_i, 5);
run <= false;
wait;
end process;

end;
19 changes: 13 additions & 6 deletions vhdl/fifo.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ entity fifo is
generic (
depth_order_g : positive;
data_width_g : positive;
prefill_g : positive := 1);
prefill_g : natural := 0);
port (
a_reset_i : in std_ulogic := '0';
a_clock_i : in std_ulogic;
Expand All @@ -35,6 +35,8 @@ use work.greycode_pack.all;

architecture rtl of fifo is

constant sync_stages_c : positive := 2;

subtype address_t is greycode_t(depth_order_g-1 downto 0);

signal a_write_address, b_read_address : address_t := (others => '0');
Expand All @@ -43,6 +45,8 @@ architecture rtl of fifo is
signal b_write_address, a_read_address : address_t;
signal a_full, b_empty : std_ulogic;

signal async_fill : integer range 0 to 2**depth_order_g-1;

begin

dual_port_ram : entity work.dual_port_ram
Expand Down Expand Up @@ -77,6 +81,7 @@ begin
sync_write_address_a_b : entity work.sync
generic map (
width_g => depth_order_g,
stages_g => sync_stages_c,
reset_value_g => '0')
port map (
reset_i => b_reset_i,
Expand All @@ -90,19 +95,19 @@ begin

b_read : process(b_reset_i, b_clock_i)
-- Compensate for sync and pipeline delay.
-- This assumes that a design that uses prefill will write (fill) continuously.
constant b_prefill_greycode_c : greycode_t(address_t'range) :=
to_greycode(std_ulogic_vector(to_unsigned(maximum(prefill_g - 3, 0), address_t'length)));
-- This assumes that fifo will be written continuously, once writing started.
constant b_prefill_c : natural := maximum(prefill_g - sync_stages_c - 1, 1);
begin
if b_reset_i = '1' then
b_read_address <= (others => '0');
b_prefill_reached <= to_stdulogic(prefill_g = 1);
b_prefill_reached <= to_stdulogic(prefill_g = 0);
elsif rising_edge(b_clock_i) then
assert (b_read_i and b_empty) = '0' report "fifo: read empty fifo";
if (b_read_i and not b_empty) = '1' then
b_read_address <= b_read_address + 1;
end if;
if b_write_address = b_prefill_greycode_c then
-- Cannot only use equal, because values may be lost in the sync.
if to_integer(unsigned(to_binary(b_write_address))) >= b_prefill_c then
b_prefill_reached <= '1';
end if;
end if;
Expand All @@ -117,5 +122,7 @@ begin
clock_i => a_clock_i,
data_i => b_read_address,
data_o => a_read_address);

async_fill <= (to_integer(unsigned(to_binary(a_write_address))) - to_integer(unsigned(to_binary(b_read_address)))) mod 2**depth_order_g;

end;
52 changes: 26 additions & 26 deletions vhdl/gearbox.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ use work.rtl_pack.all;

architecture rtl_fast of gearbox is

constant check_b_wider_a : boolean := check(b_width_g > a_width_g, "b>a");
constant check_a_wider_b : boolean := check(a_width_g > b_width_g, "a>b");

signal b_fifo_read : std_ulogic;
signal b_fifo_prefill_reached : std_ulogic;
signal b_fifo_data : std_ulogic_vector(a_width_g-1 downto 0);

signal b_barrel : std_ulogic_vector(a_width_g+b_width_g-2 downto 0);
signal b_buffer : std_ulogic_vector(a_width_g+b_width_g-2 downto 0);
signal b_index : natural range 0 to a_width_g-1;

begin
Expand All @@ -36,7 +36,7 @@ begin
generic map (
depth_order_g => fifo_depth_order_g,
data_width_g => a_width_g,
prefill_g => 2**(fifo_depth_order_g-1))
prefill_g => 2**(fifo_depth_order_g-1) - 2 * b_width_g / a_width_g)
port map (
a_reset_i => a_reset_i,
a_clock_i => a_clock_i,
Expand All @@ -47,23 +47,21 @@ begin
b_prefill_reached_o => b_fifo_prefill_reached,
b_data_o => b_fifo_data);

b_fifo_read <= to_stdulogic(b_index > a_width_g - b_width_g - 1);
b_fifo_read <= to_stdulogic(b_index > a_width_g - b_width_g - 1) and b_fifo_prefill_reached;

b_sync : process(b_reset_i, b_clock_i)
begin
if b_reset_i = '1' then
b_barrel <= (others => '-');
b_buffer <= (others => '-');
b_index <= 0;
b_data_o <= (others => '0');
elsif rising_edge(b_clock_i) then
if b_fifo_prefill_reached = '1' then
b_data_o <= b_barrel(b_index+b_width_g-1 downto b_index);
if b_fifo_read = '1' then
b_barrel(a_width_g-1 downto 0) <= b_fifo_data;
b_barrel(a_width_g+b_width_g-2 downto a_width_g) <= b_barrel(b_width_g-2 downto 0);
end if;
b_index <= (b_index + b_width_g) mod a_width_g;
b_data_o <= b_buffer(b_index+b_width_g-1 downto b_index);
if b_fifo_read = '1' then
b_buffer(a_width_g-1 downto 0) <= b_fifo_data;
b_buffer(a_width_g+b_width_g-2 downto a_width_g) <= b_buffer(b_width_g-2 downto 0);
end if;
b_index <= (b_index + b_width_g) mod a_width_g;
end if;
end process;

Expand All @@ -74,22 +72,22 @@ use work.rtl_pack.all;

architecture rtl_slow of gearbox is

constant check_a_wider_b : boolean := check(a_width_g > b_width_g, "a>b");
constant check_b_wider_a : boolean := check(b_width_g > a_width_g, "b>a");

signal a_fifo_write : std_ulogic;
signal a_fifo_data : std_ulogic_vector(b_width_g-1 downto 0);
signal b_fifo_prefill_reached : std_ulogic;

signal a_barrel : std_ulogic_vector(a_width_g+b_width_g-2 downto 0);
signal a_index : natural range 0 to a_width_g-1;
signal a_buffer : std_ulogic_vector(a_width_g+b_width_g-2 downto 0);
signal a_index : natural range 0 to b_width_g-1;

begin

fifo : entity work.fifo
generic map (
depth_order_g => fifo_depth_order_g,
data_width_g => b_width_g,
prefill_g => 2**(fifo_depth_order_g-1))
prefill_g => 2**(fifo_depth_order_g-1) - 2 * a_width_g / b_width_g)
port map (
a_reset_i => a_reset_i,
a_clock_i => a_clock_i,
Expand All @@ -102,20 +100,22 @@ begin
b_data_o => b_data_o);

a_fifo_write <= to_stdulogic(a_index > b_width_g - a_width_g - 1);
a_fifo_data <= a_barrel(b_width_g-1 downto 0);
a_fifo_data <= a_buffer(b_width_g-1 downto 0);

a_sync : process(a_reset_i, a_clock_i)
begin
if a_reset_i = '1' then
a_barrel <= (others => '-');
a_buffer <= (others => '-');
a_index <= 0;
elsif rising_edge(a_clock_i) then
if a_fifo_write = '1' then
a_barrel <= (others => '-');
a_barrel(a_width_g-2 downto 0) <= a_barrel(a_width_g+b_width_g-2 downto b_width_g);
a_buffer <= (others => '-');
if a_index /= 0 then
a_buffer(a_index-1 downto 0) <= a_buffer(a_index+b_width_g-1 downto b_width_g);
end if;
end if;
a_barrel(a_index+a_width_g-1 downto a_index) <= a_data_i;
a_index <= (a_index + a_width_g) mod a_width_g;
a_buffer(a_index+a_width_g-1 downto a_index) <= a_data_i;
a_index <= (a_index + a_width_g) mod b_width_g;
end if;
end process;

Expand All @@ -130,8 +130,8 @@ architecture rtl of gearbox is

begin

gen_slow : if a_width_g > b_width_g generate
gearbox_slow : entity work.gearbox(rtl_slow)
gen_fast : if a_width_g > b_width_g generate
gearbox_fast : entity work.gearbox(rtl_fast)
generic map (
a_width_g => a_width_g,
b_width_g => b_width_g,
Expand All @@ -145,8 +145,8 @@ begin
b_data_o => b_data_o);
end generate;

gen_fast : if b_width_g > a_width_g generate
gearbox_fast : entity work.gearbox(rtl_fast)
gen_slow : if b_width_g > a_width_g generate
gearbox_slow : entity work.gearbox(rtl_slow)
generic map (
a_width_g => a_width_g,
b_width_g => b_width_g,
Expand Down
Loading

0 comments on commit 3136a89

Please sign in to comment.