📄 wishbone_i2c_master.vhd
字号:
---- WISHBONE revB2 compiant I2C master core---- author: Richard Herveille-- rev. 0.1 based on simple_i2c-- rev. 0.2 april 27th 2001, fixed incomplete sensitivity list on assign_dato process (thanks to Matt Oseman)-- rev. 0.3 may 4th 2001, fixed typo rev.0.2 txt -> txr-- rev. 0.4 may 8th, added some remarks, fixed some sensitivity list issues------ Changes compared to simple_i2c-- 1) WISHBONE interface-- 2) added start/stop detection-- 3) added busy bit-- 4) removed automatic tri-state buffer insertion (for ASIC support)--library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_arith.all;entity wishbone_i2c_master is port ( -- wishbone signals CLK_I : in std_logic; -- master clock input RST_I : in std_logic := '0'; -- synchronous active high reset nRESET: in std_logic := '1'; -- asynchronous active low reset ADR_I : in unsigned(1 downto 0); -- lower address bits DAT_I : in std_logic_vector(15 downto 0); -- Databus input DAT_O : out std_logic_vector(15 downto 0); -- Databus output SEL_I : in std_logic_vector(1 downto 0); -- Byte select signals WE_I : in std_logic; -- Write enable input STB_I : in std_logic; -- Strobe signals / core select signal CYC_I : in std_logic; -- Valid bus cycle input ACK_O : out std_logic; -- Bus cycle acknowledge output INTA_O : out std_logic; -- interrupt request output signal -- I2C signals SCLi : in std_logic; -- I2C clock line SCLo : out std_logic; SDAi : in std_logic; -- I2C data line SDAo : out std_logic );end entity wishbone_i2c_master;architecture structural of wishbone_i2c_master is component byte_ctrl is port ( clk : in std_logic; rst : in std_logic; -- synchronous active high reset (WISHBONE compatible) nReset : in std_logic; -- asynchornous active low reset (FPGA compatible) clk_cnt : in unsigned(15 downto 0); -- 4x SCL -- input signals ena, start, stop, read, write, ack_in : std_logic; Din : in std_logic_vector(7 downto 0); -- output signals cmd_ack : out std_logic; ack_out : out std_logic; Dout : out std_logic_vector(7 downto 0); i2c_busy : out std_logic; -- i2c signals SCLi : in std_logic; -- I2C clock line SCLo : out std_logic; SDAi : in std_logic; -- I2C data line SDAo : out std_logic ); end component byte_ctrl; -- registers signal prer : unsigned(15 downto 0); -- clock prescale register signal ctr : std_logic_vector(7 downto 0); -- control register signal txr : std_logic_vector(7 downto 0); -- transmit register signal rxr : std_logic_vector(7 downto 0); -- receive register signal cr : std_logic_vector(7 downto 0); -- command register signal sr : std_logic_vector(7 downto 0); -- status register -- done signal: command completed, clear command register signal done : std_logic; -- command register signals signal sta, sto, rd, wr, ack, iack : std_logic; -- core enable signal signal core_en : std_logic; -- status register signals signal irxack, rxack : std_logic; -- received aknowledge from slave signal tip : std_logic; -- transfer in progress signal irq_flag : std_logic; -- interrupt pending flag signal i2c_busy : std_logic; -- bus busy (start signal detected)begin -- generate acknowledge output signal ACK_O <= STB_I; -- since timing is always honored -- assign DAT_O assign_dato : process(ADR_I, prer, ctr, txr, cr, rxr, sr) begin case ADR_I is when "00" => DAT_O <= std_logic_vector(prer); when "01" => DAT_O <= (x"00" & ctr); when "10" => DAT_O <= (txr & cr); when "11" => DAT_O <= (rxr & sr); when others => DAT_O <= (others => 'X'); -- for simulation only end case; end process assign_dato; -- registers block regs_block: block -- address decode signals signal we_a0, we_a1, we_a2, we_a3 : std_logic; begin -- decode address lines we_a0 <= CYC_I and STB_I and WE_I and not ADR_I(1) and not ADR_I(0); we_a1 <= CYC_I and STB_I and WE_I and not ADR_I(1) and ADR_I(0); we_a2 <= CYC_I and STB_I and WE_I and ADR_I(1) and not ADR_I(0); we_a3 <= CYC_I and STB_I and WE_I and ADR_I(1) and ADR_I(0); -- store data in writeable registers -- prescale register write_prer: process(nRESET, CLK_I) begin if (nRESET = '0') then prer <= (others => '1'); elsif (CLK_I'event and CLK_I = '1') then if (RST_I = '1') then prer <= (others => '1'); else if ( (we_a0 and SEL_I(1)) = '1') then prer(15 downto 8) <= unsigned(DAT_I(15 downto 8)); end if; if ( (we_a0 and SEL_I(0)) = '1') then prer(7 downto 0) <= unsigned(DAT_I(7 downto 0)); end if; end if; end if; end process write_prer; -- control register write_ctr: process(nRESET, CLK_I) begin if (nRESET = '0') then ctr <= (others => '0'); elsif (CLK_I'event and CLK_I = '1') then if (RST_I = '1') then ctr <= (others => '0'); else if ( (we_a1 and SEL_I(0)) = '1') then ctr <= DAT_I(7 downto 0); end if; end if; end if; end process write_ctr; -- transmit register write_txr: process(nRESET, CLK_I) begin if (nRESET = '0') then txr <= (others => '0'); elsif (CLK_I'event and CLK_I = '1') then if (RST_I = '1') then txr <= (others => '0'); else if ( (we_a2 and SEL_I(1)) = '1') then txr <= DAT_I(15 downto 8); end if; end if; end if; end process write_txr; -- command register write_cr: process(nRESET, CLK_I) begin if (nRESET = '0') then cr <= (others => '0'); -- asynchronous clear elsif (CLK_I'event and CLK_I = '1') then if (RST_I = '1') then cr <= (others => '0'); -- synchronous clear else if ( (we_a2 and SEL_I(0)) = '1') then if (core_en = '1') then cr <= DAT_I(7 downto 0); -- only take new commands when I2C core is enabled, pending commands are finished end if; else if (done = '0') then cr(7 downto 4) <= cr(7 downto 4); else cr(7 downto 0) <= (others => '0'); -- clear command_bits when command completed end if; cr(2 downto 1) <= cr(2 downto 1); cr(0) <= cr(0) and irq_flag; -- automatically clear when irq_flag is cleared end if; end if; end if; end process write_cr; end block regs_block; -- decode command register sta <= cr(7); sto <= cr(6); rd <= cr(5); wr <= cr(4); ack <= cr(3); iack <= cr(0); -- decode control register core_en <= ctr(7); -- hookup byte controller block u1: byte_ctrl port map (clk => CLK_I, rst => RST_I, nReset => nRESET, clk_cnt => prer, ena => core_en, start => sta, stop => sto, read => rd, write => wr, ack_in => ack, i2c_busy => i2c_busy, Din => txr, cmd_ack => done, ack_out => irxack, Dout => rxr, -- note: maybe store rxr in registers ?? SCLi => SCLi, SCLo => SCLo, SDAi => SDAi, SDAo => SDAo); -- status register block + interrupt request signal st_block : block begin -- generate status register bits gen_sr_bits: process (CLK_I, nRESET) begin if (nRESET = '0') then rxack <= '0'; tip <= '0'; irq_flag <= '0'; elsif (CLK_I'event and CLK_I = '1') then if (RST_I = '1') then rxack <= '0'; tip <= '0'; irq_flag <= '0'; else rxack <= irxack; tip <= ( rd or wr ); irq_flag <= (done or irq_flag) and not iack; -- interrupt request flag is always generated end if; end if; end process gen_sr_bits; -- generate interrupt request signals gen_irq: process (CLK_I, nRESET) begin if (nRESET = '0') then INTA_O <= '0'; elsif (CLK_I'event and CLK_I = '1') then if (RST_I = '1') then INTA_O <= '0'; else INTA_O <= irq_flag and ctr(6); -- interrupt signal is only generated when IEN (interrupt enable bit) is set end if; end if; end process gen_irq; -- assign status register bits sr(7) <= rxack; sr(6) <= i2c_busy; sr(5 downto 2) <= (others => '0'); -- reserved sr(1) <= tip; sr(0) <= irq_flag; end block;end architecture structural;---------------------------------------------- Byte controller section--------------------------------------------library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_arith.all;entity byte_ctrl is port ( clk : in std_logic; rst : in std_logic; -- synchronous active high reset (WISHBONE compatible) nReset : in std_logic; -- asynchornous active low reset (FPGA compatible) clk_cnt : in unsigned(15 downto 0); -- 4x SCL -- input signals ena, start, stop, read, write, ack_in : std_logic; Din : in std_logic_vector(7 downto 0); -- output signals cmd_ack : out std_logic; ack_out : out std_logic; Dout : out std_logic_vector(7 downto 0); i2c_busy : out std_logic; -- i2c signals SCLi : in std_logic; -- I2C clock line SCLo : out std_logic; SDAi : in std_logic; -- I2C data line SDAo : out std_logic );end entity byte_ctrl;architecture structural of byte_ctrl is component bit_ctrl is port ( clk : in std_logic; rst : in std_logic; nReset : in std_logic; clk_cnt : in unsigned(15 downto 0); -- clock prescale value ena : in std_logic; -- core enable signal cmd : in std_logic_vector(2 downto 0); cmd_ack : out std_logic; busy : out std_logic; Din : in std_logic; Dout : out std_logic; SCLin : in std_logic; -- I2C clock line SCLout : out std_logic; SDAin : in std_logic; -- I2C data line SDAout : out std_logic ); end component bit_ctrl; -- commands for i2c_core constant CMD_NOP : std_logic_vector(2 downto 0) := "000"; constant CMD_START : std_logic_vector(2 downto 0) := "010"; constant CMD_STOP : std_logic_vector(2 downto 0) := "011"; constant CMD_READ : std_logic_vector(2 downto 0) := "100"; constant CMD_WRITE : std_logic_vector(2 downto 0) := "101"; -- signals for bit_controller signal core_cmd : std_logic_vector(2 downto 0); signal core_ack, core_txd, core_rxd : std_logic; -- signals for shift register signal sr : std_logic_vector(7 downto 0); -- 8bit shift register signal shift, ld : std_logic; -- signals for state machine signal go, host_ack : std_logic;begin -- hookup bit_controller u1: bit_ctrl port map (clk, rst, nReset, clk_cnt, ena, core_cmd, core_ack, i2c_busy, core_txd, core_rxd, SCLi, SCLo, SDAi, SDAo); -- generate host-command-acknowledge cmd_ack <= host_ack; -- generate go-signal go <= (read or write) and not host_ack; -- assign Dout output to shift-register Dout <= sr; -- assign ack_out output to core_rxd (contains last received bit) ack_out <= core_rxd; -- generate shift register shift_register: process(clk) begin if (clk'event and clk = '1') then if (ld = '1') then sr <= din; elsif (shift = '1') then sr <= (sr(6 downto 0) & core_rxd); end if; end if; end process shift_register; -- -- state machine -- statemachine : block type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop); signal state : states; signal dcnt : unsigned(2 downto 0); begin -- -- command interpreter, translate complex commands into simpler I2C commands -- nxt_state_decoder: process(clk, nReset, state) variable nxt_state : states; variable idcnt : unsigned(2 downto 0); variable ihost_ack : std_logic; variable icore_cmd : std_logic_vector(2 downto 0); variable icore_txd : std_logic; variable ishift, iload : std_logic; begin -- 8 databits (1byte) of data to shift-in/out idcnt := dcnt; -- no acknowledge (until command complete) ihost_ack := '0'; icore_txd := core_txd; -- keep current command to bit_controller icore_cmd := core_cmd; -- no shifting or loading of shift-register ishift := '0'; iload := '0'; -- keep current state; nxt_state := state; case state is when st_idle => if (go = '1') then if (start = '1') then nxt_state := st_start; icore_cmd := CMD_START; elsif (read = '1') then nxt_state := st_read; icore_cmd := CMD_READ; idcnt := "111"; else nxt_state := st_write; icore_cmd := CMD_WRITE; idcnt := "111"; iload := '1'; end if; end if; when st_start => if (core_ack = '1') then if (read = '1') then nxt_state := st_read; icore_cmd := CMD_READ; idcnt := "111"; else nxt_state := st_write; icore_cmd := CMD_WRITE; idcnt := "111"; iload := '1'; end if; end if; when st_write => if (core_ack = '1') then idcnt := dcnt -1; -- count down Data_counter icore_txd := sr(7); if (dcnt = 0) then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -