[rtl] update ONEWIRE module

🐛 fixing a regressions: busy signal was stuck at zero
This commit is contained in:
stnolting 2024-12-03 19:14:51 +01:00
parent d307bf5fa1
commit 8da24440c6

View file

@ -16,6 +16,9 @@ library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_onewire is
generic (
ONEWIRE_FIFO : natural range 1 to 2**15 -- RTX fifo depth, has to be a power of two, min 1
);
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active
@ -25,13 +28,14 @@ entity neorv32_onewire is
clkgen_i : in std_ulogic_vector(7 downto 0);
onewire_i : in std_ulogic; -- 1-wire line state
onewire_o : out std_ulogic; -- 1-wire line pull-down
irq_o : out std_ulogic -- transfer done IRQ
irq_o : out std_ulogic -- transfer done IRQ
);
end neorv32_onewire;
architecture neorv32_onewire_rtl of neorv32_onewire is
-- timing configuration (absolute time in multiples of the base tick time t_base) --
-- timing configuration (known-good, no need to change) --
-- absolute time in multiples of the base tick time t_base; see data sheet for more information about the t* timing values
constant t_write_one_c : unsigned(6 downto 0) := to_unsigned( 1, 7); -- t0
constant t_read_sample_c : unsigned(6 downto 0) := to_unsigned( 2, 7); -- t1
constant t_slot_end_c : unsigned(6 downto 0) := to_unsigned( 7, 7); -- t2
@ -39,40 +43,62 @@ architecture neorv32_onewire_rtl of neorv32_onewire is
constant t_reset_end_c : unsigned(6 downto 0) := to_unsigned(48, 7); -- t4
constant t_presence_sample_c : unsigned(6 downto 0) := to_unsigned(55, 7); -- t5
constant t_presence_end_c : unsigned(6 downto 0) := to_unsigned(96, 7); -- t6
-- -> see data sheet for more information about the t* timing values --
-- control register --
constant ctrl_en_c : natural := 0; -- r/w: TWI enable
constant ctrl_prsc0_c : natural := 1; -- r/w: prescaler select bit 0
constant ctrl_prsc1_c : natural := 2; -- r/w: prescaler select bit 1
constant ctrl_clkdiv0_c : natural := 3; -- r/w: clock divider bit 0
constant ctrl_clkdiv7_c : natural := 10; -- r/w: clock divider bit 7
constant ctrl_trig_rst_c : natural := 11; -- -/w: trigger reset pulse, auto-clears
constant ctrl_trig_bit_c : natural := 12; -- -/w: trigger single-bit transmission, auto-clears
constant ctrl_trig_byte_c : natural := 13; -- -/w: trigger full-byte transmission, auto-clears
constant ctrl_en_c : natural := 0; -- r/w: TWI enable
constant ctrl_clear_c : natural := 1; -- -/w: clear FIFO, bit auto-clears
constant ctrl_prsc0_c : natural := 2; -- r/w: prescaler select bit 0
constant ctrl_prsc1_c : natural := 3; -- r/w: prescaler select bit 1
constant ctrl_clkdiv0_c : natural := 4; -- r/w: clock divider bit 0
constant ctrl_clkdiv7_c : natural := 11; -- r/w: clock divider bit 7
--
constant ctrl_sense_c : natural := 29; -- r/-: current state of the bus line
constant ctrl_presence_c : natural := 30; -- r/-: bus presence detected
constant ctrl_busy_c : natural := 31; -- r/-: set while operation in progress
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(fifo size), bit 3 (msb)
--
constant ctrl_tx_full_c : natural := 28; -- r/-: TX FIFO full
constant ctrl_rx_avail_c : natural := 29; -- r/-: RX FIFO data available
constant ctrl_sense_c : natural := 30; -- r/-: current state of the bus line
constant ctrl_busy_c : natural := 31; -- r/-: set while operation in progress
-- data/command register --
constant dcmd_lsb_c : natural := 0; -- r/w: RX/TX data MSB
constant dcmd_msb_c : natural := 7; -- r/w: RX/TX data MSB
constant dcmd_cmd_lo_c : natural := 8; -- -/w: operation command
constant dcmd_cmd_hi_c : natural := 9; -- -/w: operation command
constant dcmd_pres_c : natural := 10; -- r/-: bus presence detected
-- commands --
constant cmd_nop_c : std_ulogic_vector(1 downto 0) := "00"; -- do nothing
constant cmd_bit_c : std_ulogic_vector(1 downto 0) := "01"; -- trigger single-bit transmission
constant cmd_byt_c : std_ulogic_vector(1 downto 0) := "10"; -- trigger full-byte transmission
constant cmd_rst_c : std_ulogic_vector(1 downto 0) := "11"; -- trigger reset pulse and sample presence
-- control register --
type ctrl_t is record
enable : std_ulogic;
clear : std_ulogic;
clk_prsc : std_ulogic_vector(1 downto 0);
clk_div : std_ulogic_vector(7 downto 0);
trig_rst : std_ulogic;
trig_bit : std_ulogic;
trig_byte : std_ulogic;
end record;
signal ctrl : ctrl_t;
-- write data --
signal tx_data : std_ulogic_vector(7 downto 0);
-- FIFO interface --
type fifo_t is record
rx_clr, tx_clr : std_ulogic;
rx_we, tx_we : std_ulogic;
rx_re, tx_re : std_ulogic;
rx_rdata, rx_wdata : std_ulogic_vector(8 downto 0);
tx_rdata, tx_wdata : std_ulogic_vector(9 downto 0);
rx_avail, tx_avail : std_ulogic;
rx_free, tx_free : std_ulogic;
end record;
signal fifo : fifo_t;
-- clock generator --
signal clk_sel : std_ulogic_vector(3 downto 0);
signal clk_tick : std_ulogic;
signal clk_cnt : unsigned(7 downto 0);
signal clk_src : std_ulogic_vector(3 downto 0);
signal clk_cnt : unsigned(7 downto 0);
signal clk_tick : std_ulogic;
signal clk_tick2 : std_ulogic;
-- serial engine --
type serial_t is record
@ -80,14 +106,13 @@ architecture neorv32_onewire_rtl of neorv32_onewire is
busy : std_ulogic;
bit_cnt : unsigned(2 downto 0);
tick_cnt : unsigned(6 downto 0);
tick : std_ulogic;
tick_ff : std_ulogic;
sreg : std_ulogic_vector(7 downto 0);
wire_in : std_ulogic_vector(1 downto 0);
wire_lo : std_ulogic;
wire_hi : std_ulogic;
sample : std_ulogic;
presence : std_ulogic;
done : std_ulogic;
end record;
signal serial : serial_t;
@ -98,38 +123,23 @@ begin
bus_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
ctrl.enable <= '0';
ctrl.clk_prsc <= (others => '0');
ctrl.clk_div <= (others => '0');
ctrl.trig_rst <= '0';
ctrl.trig_bit <= '0';
ctrl.trig_byte <= '0';
tx_data <= (others => '0');
bus_rsp_o <= rsp_terminate_c;
ctrl.enable <= '0';
ctrl.clk_prsc <= (others => '0');
ctrl.clk_div <= (others => '0');
ctrl.clear <= '0';
elsif rising_edge(clk_i) then
-- bus handshake --
bus_rsp_o.ack <= bus_req_i.stb;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');
-- write access --
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') then
if (bus_req_i.addr(2) = '0') then -- control register
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.clk_prsc <= bus_req_i.data(ctrl_prsc1_c downto ctrl_prsc0_c);
ctrl.clk_div <= bus_req_i.data(ctrl_clkdiv7_c downto ctrl_clkdiv0_c);
else -- data register
tx_data <= bus_req_i.data(7 downto 0);
end if;
end if;
-- operation triggers --
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '0') then -- set by host
ctrl.trig_rst <= bus_req_i.data(ctrl_trig_rst_c);
ctrl.trig_bit <= bus_req_i.data(ctrl_trig_bit_c);
ctrl.trig_byte <= bus_req_i.data(ctrl_trig_byte_c);
elsif (ctrl.enable = '0') or (serial.state(1) = '1') then -- cleared when disabled or when in RTX/RESET state
ctrl.trig_rst <= '0';
ctrl.trig_bit <= '0';
ctrl.trig_byte <= '0';
-- control register write access --
ctrl.clear <= '0';
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '0') then -- control register
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.clear <= bus_req_i.data(ctrl_clear_c);
ctrl.clk_prsc <= bus_req_i.data(ctrl_prsc1_c downto ctrl_prsc0_c);
ctrl.clk_div <= bus_req_i.data(ctrl_clkdiv7_c downto ctrl_clkdiv0_c);
end if;
-- read access --
if (bus_req_i.stb = '1') and (bus_req_i.rw = '0') then
@ -138,10 +148,15 @@ begin
bus_rsp_o.data(ctrl_prsc1_c downto ctrl_prsc0_c) <= ctrl.clk_prsc;
bus_rsp_o.data(ctrl_clkdiv7_c downto ctrl_clkdiv0_c) <= ctrl.clk_div;
--
bus_rsp_o.data(ctrl_sense_c) <= serial.wire_in(1);
bus_rsp_o.data(ctrl_presence_c) <= serial.presence;
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(ONEWIRE_FIFO), 4));
--
bus_rsp_o.data(ctrl_tx_full_c) <= not fifo.tx_free;
bus_rsp_o.data(ctrl_rx_avail_c) <= fifo.rx_avail;
bus_rsp_o.data(ctrl_sense_c) <= serial.wire_in(1);
bus_rsp_o.data(ctrl_busy_c) <= fifo.tx_avail or serial.busy;
else -- data register
bus_rsp_o.data(7 downto 0) <= serial.sreg;
bus_rsp_o.data(dcmd_msb_c downto dcmd_lsb_c) <= fifo.rx_rdata(7 downto 0);
bus_rsp_o.data(dcmd_pres_c) <= fifo.rx_rdata(8);
end if;
end if;
@ -149,40 +164,114 @@ begin
end process bus_access;
-- Tick Generator -------------------------------------------------------------------------
-- Data FIFO ("Ring Buffer") --------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
tick_generator: process(rstn_i, clk_i)
-- TX FIFO --
tx_fifo_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => ONEWIRE_FIFO,
FIFO_WIDTH => 10, -- 2-bit command + 8-bit data
FIFO_RSYNC => true,
FIFO_SAFE => true,
FULL_RESET => false
)
port map (
-- control --
clk_i => clk_i,
rstn_i => rstn_i,
clear_i => fifo.tx_clr,
half_o => open,
-- write port --
wdata_i => fifo.tx_wdata,
we_i => fifo.tx_we,
free_o => fifo.tx_free,
-- read port --
re_i => fifo.tx_re,
rdata_o => fifo.tx_rdata,
avail_o => fifo.tx_avail
);
fifo.tx_clr <= '1' when (ctrl.enable = '0') or (ctrl.clear = '1') else '0';
fifo.tx_we <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '1') else '0';
fifo.tx_wdata <= bus_req_i.data(dcmd_cmd_hi_c downto dcmd_cmd_lo_c) & bus_req_i.data(dcmd_msb_c downto dcmd_lsb_c);
fifo.tx_re <= '1' when (serial.state = "101") and (clk_tick = '1') else '0';
-- RX FIFO --
rx_fifo_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => ONEWIRE_FIFO,
FIFO_WIDTH => 9, -- 1-bit presence status + 8-bit data
FIFO_RSYNC => true,
FIFO_SAFE => true,
FULL_RESET => false
)
port map (
-- control --
clk_i => clk_i,
rstn_i => rstn_i,
clear_i => fifo.rx_clr,
half_o => open,
-- write port --
wdata_i => fifo.rx_wdata,
we_i => fifo.rx_we,
free_o => fifo.rx_free,
-- read port --
re_i => fifo.rx_re,
rdata_o => fifo.rx_rdata,
avail_o => fifo.rx_avail
);
fifo.rx_clr <= '1' when (ctrl.enable = '0') or (ctrl.clear = '1') else '0';
fifo.rx_wdata <= serial.presence & serial.sreg;
fifo.rx_we <= serial.done;
fifo.rx_re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') and (bus_req_i.addr(2) = '1') else '0';
-- IRQ if enabled and TX FIFO is empty and serial engine is idle --
irq_generator: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
clk_tick <= '0';
clk_cnt <= (others => '0');
serial.tick <= '0';
serial.tick_ff <= '0';
irq_o <= '0';
elsif rising_edge(clk_i) then
clk_tick <= clk_sel(to_integer(unsigned(ctrl.clk_prsc)));
serial.tick <= '0'; -- default
irq_o <= ctrl.enable and (not fifo.tx_avail) and (not serial.busy);
end if;
end process irq_generator;
-- Clock Generator ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
clock_generator: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
clk_cnt <= (others => '0');
clk_tick <= '0';
clk_tick2 <= '0';
elsif rising_edge(clk_i) then
clk_tick <= '0'; -- default
if (ctrl.enable = '0') then
clk_cnt <= (others => '0');
elsif (clk_tick = '1') then
elsif (clk_src(to_integer(unsigned(ctrl.clk_prsc))) = '1') then
if (clk_cnt = unsigned(ctrl.clk_div)) then
clk_cnt <= (others => '0');
serial.tick <= '1'; -- signal is high for 1 clk_i cycle every 't_base'
clk_cnt <= (others => '0');
clk_tick <= '1'; -- signal is high for 1 clk_i cycle every 't_base'
else
clk_cnt <= clk_cnt + 1;
end if;
end if;
serial.tick_ff <= serial.tick; -- tick delayed by one clock cycle (for precise bus state sampling)
clk_tick2 <= clk_tick; -- tick delayed by one clock cycle (for precise bus state sampling)
end if;
end process tick_generator;
end process clock_generator;
-- enable SoC clock generator --
clkgen_en_o <= ctrl.enable;
-- only use the lowest 4 clocks of the system clock generator --
clk_sel <= clkgen_i(3 downto 0);
clk_src <= clkgen_i(3 downto 0);
-- Serial Engine --------------------------------------------------------------------------
-- Serial Engine (1-Wire PHY) -------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
serial_engine: process(rstn_i, clk_i)
begin
@ -195,6 +284,7 @@ begin
serial.bit_cnt <= (others => '0');
serial.sreg <= (others => '0');
serial.sample <= '0';
serial.done <= '0';
onewire_o <= '0';
elsif rising_edge(clk_i) then
-- input synchronizer --
@ -210,6 +300,7 @@ begin
-- defaults --
serial.wire_lo <= '0';
serial.wire_hi <= '0';
serial.done <= '0';
-- FSM --
serial.state(2) <= ctrl.enable; -- module enabled? force reset state otherwise
@ -218,27 +309,26 @@ begin
when "100" => -- enabled, but IDLE: wait for new request
-- ------------------------------------------------------------
serial.tick_cnt <= (others => '0');
-- transmission size --
if (ctrl.trig_bit = '1') then
serial.bit_cnt <= "000"; -- single bit
else
serial.bit_cnt <= "111"; -- full-byte
end if;
-- any operation request? --
if (ctrl.trig_rst = '1') or (ctrl.trig_bit = '1') or (ctrl.trig_byte = '1') then
if (fifo.tx_avail = '1') then -- new command/data available?
serial.state(1 downto 0) <= "01"; -- SYNC
end if;
when "101" => -- SYNC: start operation with next base tick
-- ------------------------------------------------------------
serial.sreg <= tx_data;
if (serial.tick = '1') then -- synchronize
if (fifo.tx_rdata(dcmd_cmd_hi_c downto dcmd_cmd_lo_c) = cmd_bit_c) then
serial.bit_cnt <= "000"; -- single bit
else
serial.bit_cnt <= "111"; -- full byte
end if;
serial.sreg <= fifo.tx_rdata(dcmd_msb_c downto dcmd_lsb_c);
if (clk_tick = '1') then -- synchronize
serial.wire_lo <= '1'; -- force bus to low
if (ctrl.trig_rst = '1') then
serial.state(1 downto 0) <= "11"; -- RESET
else
serial.state(1 downto 0) <= "10"; -- RTX
end if;
case fifo.tx_rdata(dcmd_cmd_hi_c downto dcmd_cmd_lo_c) is -- operation command
when cmd_bit_c => serial.state(1 downto 0) <= "10"; -- RTX (single bit)
when cmd_byt_c => serial.state(1 downto 0) <= "10"; -- RTX (full byte)
when cmd_rst_c => serial.state(1 downto 0) <= "11"; -- RESET
when others => serial.state(1 downto 0) <= "00"; -- IDLE (NOP)
end case;
end if;
when "110" => -- RTX: read/write 'serial.bit_cnt-1' bits
@ -248,26 +338,27 @@ begin
serial.wire_hi <= '1'; -- release bus
end if;
-- sample input (precisely / just once!) --
if (serial.tick_cnt = t_read_sample_c) and (serial.tick_ff = '1') then
if (serial.tick_cnt = t_read_sample_c) and (clk_tick2 = '1') then
serial.sample <= serial.wire_in(1);
end if;
-- inter-slot pause (end of bit) & iteration control --
-- inter-slot pause (end of bit) and iteration control --
if (serial.tick_cnt = t_pause_end_c) then -- bit done
serial.tick_cnt <= (others => '0');
serial.sreg <= serial.sample & serial.sreg(7 downto 1); -- new bit; LSB first
serial.bit_cnt <= serial.bit_cnt - 1;
if (serial.bit_cnt = "000") then -- all done
serial.state(1 downto 0) <= "00"; -- go back to IDLE
serial.done <= '1';
serial.state(1 downto 0) <= "00"; -- IDLE
else -- next bit
serial.wire_lo <= '1'; -- force bus to low again
end if;
elsif (serial.tick = '1') then
elsif (clk_tick = '1') then
serial.tick_cnt <= serial.tick_cnt + 1;
end if;
when "111" => -- RESET: generate reset pulse and check for bus presence
-- ------------------------------------------------------------
if (serial.tick = '1') then
if (clk_tick = '1') then
serial.tick_cnt <= serial.tick_cnt + 1;
end if;
-- end of reset pulse --
@ -275,12 +366,13 @@ begin
serial.wire_hi <= '1'; -- release bus
end if;
-- sample device presence (precisely / just once!) --
if (serial.tick_cnt = t_presence_sample_c) and (serial.tick_ff = '1') then
if (serial.tick_cnt = t_presence_sample_c) and (clk_tick2 = '1') then
serial.presence <= not serial.wire_in(1); -- set if bus is pulled low by any device
end if;
-- end of presence phase --
if (serial.tick_cnt = t_presence_end_c) then
serial.state(1 downto 0) <= "00"; -- go back to IDLE
serial.done <= '1';
serial.state(1 downto 0) <= "00"; -- IDLE
end if;
when others => -- "0--" OFFLINE: deactivated, reset externally-readable signals
@ -297,20 +389,4 @@ begin
serial.busy <= '0' when (serial.state(1 downto 0) = "00") else '1';
-- Interrupt Generator --------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
irq_generator: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_o <= '0';
elsif rising_edge(clk_i) then
if (serial.state = "100") then -- enabled and in idle state
irq_o <= '1';
else
irq_o <= '0';
end if;
end if;
end process irq_generator;
end neorv32_onewire_rtl;