M src/i2c/address_generator.vhd => src/i2c/address_generator.vhd +1 -1
@@ 52,7 52,7 @@ begin -- architecture a1
curr_scl;
done_o <= curr_done and not next_done;
- next_done <= '1' when curr_state = ACK and next_state /= ACK else
+ next_done <= '1' when curr_state = ACK and scl_rising_i = '1' else
'1' when curr_done = '1' and scl_falling_delayed_i = '0' else
'0';
M src/i2c/master.vhd => src/i2c/master.vhd +1 -1
@@ 91,7 91,6 @@ architecture a1 of master is
signal waiting_for_data : std_logic;
signal scl_gen_falling : std_logic;
begin -- architecture a1
- dev_busy_o <= transmitting or receiving;
bus_busy_o <= bus_busy;
waiting_for_data <= tx_scl_stretch or rx_scl_stretch;
waiting_o <= waiting_for_data;
@@ 277,6 276,7 @@ begin -- architecture a1
address_gen_o => adr_gen,
receive_o => receiving,
transmit_o => transmitting,
+ dev_busy_o => dev_busy_o,
bus_busy_o => bus_busy);
end architecture a1;
M src/i2c/master_state.vhd => src/i2c/master_state.vhd +2 -0
@@ 46,6 46,7 @@ entity master_state is
address_gen_o : out std_logic;
receive_o : out std_logic;
transmit_o : out std_logic;
+ dev_busy_o : out std_logic;
bus_busy_o : out std_logic);
end entity master_state;
@@ 101,6 102,7 @@ begin -- architecture a1
req_start_o <= '1' when curr_state = GENERATING_START or curr_state = GENERATED_START else '0';
req_stop_o <= '1' when curr_state = GENERATING_STOP or curr_state = GENERATED_STOP else '0';
cond_gen_o <= cond_gen;
+ dev_busy_o <= '1' when curr_state /= IDLE and curr_state /= BUS_BUSY and curr_state /= ERR else '0';
address_gen_start_o <= '1' when curr_state = GENERATED_START and next_state = GENERATING_ADDRESS else '0';
M src/i2c/scl_generator.vhd => src/i2c/scl_generator.vhd +0 -5
@@ 43,9 43,6 @@ architecture a1 of scl_generator is
signal curr_scl_enable : std_logic := '0';
signal next_scl_enable : std_logic;
-
- signal curr_requested_change : std_logic;
- signal next_requested_change : std_logic;
begin -- architecture a1
cannot_comply_o <= '1' when curr_state = CHANGING and scl_changing /= '1' else '0';
@@ 90,13 87,11 @@ begin -- architecture a1
if rst_in = '0' then -- synchronous reset (active low)
curr_stable_count <= 0;
curr_scl_enable <= '0';
- curr_requested_change <= '0';
curr_state <= OBSERVE;
curr_scl <= '1';
else
curr_stable_count <= next_stable_count;
curr_scl_enable <= next_scl_enable;
- curr_requested_change <= next_requested_change;
curr_state <= next_state;
curr_scl <= next_scl;
end if;
M src/i2c/tx.vhd => src/i2c/tx.vhd +6 -4
@@ 185,10 185,6 @@ begin -- architecture a1
curr_saving_buffer_index <= 0;
curr_scl <= '1'; -- assume 1 (the default, no one transmitting)
curr_done <= next_done;
- elsif rst_i2c_i = '1' then
- curr_state <= IDLE;
- curr_scl <= '1'; -- assume 1 (the default, no one transmitting)
- curr_done <= '0';
else
curr_state <= next_state;
curr_tx_buffers <= next_tx_buffers;
@@ 198,6 194,12 @@ begin -- architecture a1
curr_scl <= next_scl;
curr_done <= next_done;
end if;
+
+ if rst_i2c_i = '1' then
+ curr_state <= IDLE;
+ curr_scl <= '1'; -- assume 1 (the default, no one transmitting)
+ curr_done <= '0';
+ end if;
end if;
end process set_regs;
A src/ssd1306/counter.vhd => src/ssd1306/counter.vhd +299 -0
@@ 0,0 1,299 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library i2c;
+library utils;
+
+use work.ssd1306_pkg.all;
+
+entity ssd1306_counter is
+
+ generic (
+ CLK_FREQ : integer := 100000000; -- Input clock frequency
+ I2C_CLK_FREQ : integer := 5000000;
+ COUNT_FREQ : integer := 1;
+ DELAY : integer := 20;
+ EXPECT_ACK : std_logic := '1';
+ SCL_MIN_STABLE_CYCLES : natural := 50);
+
+ port (
+ clk_i : in std_logic;
+ rst_i : in std_logic;
+
+ start_i : in std_logic;
+
+ err_noack_data_o : out std_logic;
+ err_noack_address_o : out std_logic;
+ err_arbitration_o : out std_logic;
+ err_general_o : out std_logic;
+
+ state_o : out std_logic_vector(3 downto 0);
+ substate_o : out std_logic_vector(2 downto 0);
+
+ bus_busy_o : out std_logic;
+ dev_busy_o : out std_logic;
+ waiting_o : out std_logic;
+
+ sda_io : inout std_logic;
+ scl_io : inout std_logic);
+
+end entity ssd1306_counter;
+
+architecture a1 of ssd1306_counter is
+ constant ADDRESS : std_logic_vector(6 downto 0) := "0111100";
+
+ signal rst_n : std_logic;
+ signal rst_sync : std_logic;
+
+ signal i2c_clk : std_logic;
+
+ signal master_start, master_stop, master_run : std_logic;
+ signal rw : std_logic;
+
+ signal tx_valid, tx_ready, tx_clear_buffer : std_logic;
+ signal tx_data : std_logic_vector(7 downto 0);
+
+ signal rx_valid, rx_confirm : std_logic;
+ signal rx_data : std_logic_vector(7 downto 0);
+
+ signal err_noack_data : std_logic;
+ signal err_noack_address : std_logic;
+ signal err_arbitration : std_logic;
+ signal err_general : std_logic;
+ signal any_err : std_logic;
+
+ signal waiting : std_logic;
+ signal dev_busy : std_logic;
+
+ signal sda, scl : std_logic;
+ signal sda_enable, scl_enable : std_logic;
+
+ type state_t is (IDLE, INIT_DISPLAY, INIT_ADDRESSING_MODE, ZERO_OUT_RAM, SET_ADR_ZERO, DIGIT_N, SEC_DELAY);
+ signal curr_state : state_t;
+ signal next_state : state_t;
+
+ signal curr_digit : natural;
+ signal next_digit : natural;
+
+ type substate_t is (IDLE, START, DATA, STOP, ALT);
+ signal curr_substate : substate_t;
+ signal next_substate : substate_t;
+
+ constant DIGITS : natural := 3;
+ signal count : std_logic_vector(DIGITS * 4 - 1 downto 0);
+
+ signal digit_data : data_arr_t(0 to 8);
+
+ signal curr_index : natural;
+ signal next_index : natural;
+ signal max_index : natural;
+begin -- architecture a+
+
+ state_o <= std_logic_vector(to_unsigned(state_t'pos(curr_state), 4));
+ substate_o <= std_logic_vector(to_unsigned(substate_t'pos(curr_substate), 3));
+
+ tx_clear_buffer <= '0';
+ master_run <= '1';
+ rst_n <= not rst_sync;
+ waiting_o <= waiting;
+ dev_busy_o <= dev_busy;
+
+ err_noack_data_o <= err_noack_data;
+ err_noack_address_o <= err_noack_address;
+ err_arbitration_o <= err_arbitration;
+ err_general_o <= err_general;
+ any_err <= err_general or err_arbitration or err_noack_address or err_noack_data;
+
+ rw <= '0';
+
+ max_index <= SSD1306_INIT'length when curr_state = INIT_DISPLAY else
+ SSD1306_HORIZONTAL_ADDRESSING_MODE'length when curr_state = INIT_ADDRESSING_MODE else
+ 9 when curr_state = DIGIT_N else
+ SSD1306_CURSOR_TO_ZERO'length when curr_state = SET_ADR_ZERO else
+ I2C_CLK_FREQ/2 when curr_state = SEC_DELAY else
+ SSD1306_ZERO_LEN when curr_state = ZERO_OUT_RAM else
+ 0;
+
+ next_index <= 0 when curr_substate = IDLE else
+ (curr_index + 1) when curr_substate = ALT and curr_index < max_index else
+ (curr_index + 1) when tx_ready = '1' and curr_index < max_index and curr_substate = DATA else
+ curr_index;
+
+ tx_valid <= '1' when tx_ready = '1' and curr_index < max_index and curr_substate = DATA else '0';
+
+ tx_data <= SSD1306_INIT(curr_index) when curr_state = INIT_DISPLAY and curr_index < max_index else
+ SSD1306_HORIZONTAL_ADDRESSING_MODE(curr_index) when curr_state = INIT_ADDRESSING_MODE and curr_index < max_index else
+ SSD1306_ZERO(0) when curr_state = ZERO_OUT_RAM and curr_index = 0 else
+ SSD1306_ZERO(1) when curr_state = ZERO_OUT_RAM and curr_index /= 0 else
+ SSD1306_CURSOR_TO_ZERO(curr_index) when curr_state = SET_ADR_ZERO and curr_index < max_index else
+
+ digit_data(curr_index) when curr_state = DIGIT_N and curr_index < max_index else
+ "00000000";
+
+ next_digit <= 0 when curr_state /= DIGIT_N else
+ (curr_digit + 1) when curr_state = DIGIT_N and curr_substate = STOP and curr_digit < DIGITS - 1 else
+ curr_digit;
+
+ digit_data <= ssd1306_bcd_digit_data(count(((DIGITS - 1 - curr_digit) + 1) * 4 - 1 downto (DIGITS - 1 - curr_digit) * 4));
+
+ master_start <= '1' when curr_substate = START else '0';
+ master_stop <= '1' when curr_substate = STOP else '0';
+
+ set_next_state: process (all) is
+ begin -- process set_next_state
+ next_state <= curr_state;
+
+ if curr_state = IDLE then
+ next_state <= INIT_DISPLAY;
+ elsif curr_state = INIT_DISPLAY then
+ if curr_substate = STOP then
+ next_state <= INIT_ADDRESSING_MODE;
+ end if;
+ elsif curr_state = INIT_ADDRESSING_MODE then
+ if curr_substate = STOP then
+ next_state <= ZERO_OUT_RAM;
+ end if;
+ elsif curr_state = ZERO_OUT_RAM then
+ if curr_substate = STOP then
+ next_state <= SET_ADR_ZERO;
+ end if;
+ elsif curr_state = SET_ADR_ZERO then
+ if curr_substate = STOP then
+ next_state <= DIGIT_N;
+ end if;
+ elsif curr_state = DIGIT_N then
+ if curr_substate = STOP and curr_digit = DIGITS - 1 then
+ next_state <= SEC_DELAY;
+ end if;
+ elsif curr_state = SEC_DELAY then
+ if curr_index = max_index then
+ next_state <= SET_ADR_ZERO;
+ end if;
+ end if;
+
+ if any_err = '1' then
+ next_state <= IDLE;
+ end if;
+ end process set_next_state;
+
+ set_next_substate: process (all) is
+ begin -- process set_next_state
+ next_substate <= curr_substate;
+
+ if curr_state /= IDLE and curr_state /= SEC_DELAY then
+ if curr_substate = IDLE then
+ if dev_busy = '0' then
+ next_substate <= START;
+ end if;
+ elsif curr_substate = START then
+ next_substate <= DATA;
+ elsif curr_substate = DATA and curr_index = max_index then
+ if waiting = '1' then
+ next_substate <= STOP;
+ end if;
+ elsif curr_substate = STOP then
+ next_substate <= IDLE;
+ elsif curr_substate = ALT then
+ next_substate <= IDLE;
+ end if;
+ else
+ next_substate <= ALT;
+ end if;
+ end process set_next_substate;
+
+ counter: entity utils.counter
+ generic map (
+ MAX => 9,
+ DIGITS => DIGITS,
+ IN_FREQ => CLK_FREQ,
+ OUT_FREQ => COUNT_FREQ)
+ port map (
+ clk_i => clk_i,
+ rst_in => rst_n,
+ value_o => count);
+
+ divider: entity utils.clock_divider
+ generic map (
+ IN_FREQ => CLK_FREQ,
+ OUT_FREQ => I2C_CLK_FREQ)
+ port map (
+ clk_i => clk_i,
+ clk_o => i2c_clk);
+
+ i2c_master: entity i2c.master
+ generic map (
+ SCL_FALLING_DELAY => DELAY,
+ SCL_MIN_STABLE_CYCLES => SCL_MIN_STABLE_CYCLES)
+ port map (
+ clk_i => i2c_clk,
+ rst_in => rst_n,
+--
+ slave_address_i => ADDRESS,
+--
+ generate_ack_i => '1',
+ expect_ack_i => EXPECT_ACK,
+--
+ rx_valid_o => rx_valid,
+ rx_data_o => rx_data,
+ rx_confirm_i => rx_confirm,
+--
+ tx_ready_o => tx_ready,
+ tx_valid_i => tx_valid,
+ tx_data_i => tx_data,
+ tx_clear_buffer_i => tx_clear_buffer,
+--
+ err_noack_data_o => err_noack_data,
+ err_noack_address_o => err_noack_address,
+ err_arbitration_o => err_arbitration,
+ err_general_o => err_general,
+--
+ stop_i => master_stop,
+ start_i => master_start,
+ run_i => master_run,
+ rw_i => rw,
+--
+ dev_busy_o => dev_busy,
+ bus_busy_o => bus_busy_o,
+ waiting_o => waiting,
+--
+ sda_i => sda,
+ scl_i => scl,
+ sda_enable_o => sda_enable,
+ scl_enable_o => scl_enable);
+
+ sync_reset: entity utils.metastability_filter
+ port map (
+ clk_i => clk_i,
+ signal_i => rst_i,
+ signal_o => rst_sync);
+
+ sda_open_buffer: entity utils.open_drain_buffer
+ port map (
+ pad_io => sda_io,
+ enable_i => sda_enable,
+ state_o => sda);
+ scl_open_buffer: entity utils.open_drain_buffer
+ port map (
+ pad_io => scl_io,
+ enable_i => scl_enable,
+ state_o => scl);
+
+ set_regs: process (i2c_clk) is
+ begin -- process set_regs
+ if rising_edge(i2c_clk) then -- rising clock edge
+ if rst_n = '0' then -- synchronous reset (active low)
+ curr_state <= IDLE;
+ curr_substate <= IDLE;
+ curr_digit <= 0;
+ curr_index <= 0;
+ else
+ curr_state <= next_state;
+ curr_substate <= next_substate;
+ curr_digit <= next_digit;
+ curr_index <= next_index;
+ end if;
+ end if;
+ end process set_regs;
+
+end architecture a1;
A src/ssd1306/full_on.vhd => src/ssd1306/full_on.vhd +196 -0
@@ 0,0 1,196 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library i2c;
+library utils;
+
+entity full_on is
+
+ generic (
+ CLK_FREQ : integer := 100000000; -- Input clock frequency
+ I2C_CLK_FREQ : integer := 5000000;
+ DELAY : integer := 20;
+
+ SCL_MIN_STABLE_CYCLES : natural := 50);
+
+ port (
+ clk_i : in std_logic;
+ rst_i : in std_logic;
+
+ start_i : in std_logic;
+
+ err_noack_data_o : out std_logic;
+ err_noack_address_o : out std_logic;
+ err_arbitration_o : out std_logic;
+ err_general_o : out std_logic;
+
+ full_on_state_o : out std_logic_vector(2 downto 0);
+
+ bus_busy_o : out std_logic;
+ dev_busy_o : out std_logic;
+ waiting_o : out std_logic;
+
+ sda_io : inout std_logic;
+ scl_io : inout std_logic);
+
+end entity full_on;
+
+architecture a1 of full_on is
+ constant ADDRESS : std_logic_vector(6 downto 0) := "0111100";
+
+ type data_arr_t is array (natural range <>) of std_logic_vector(7 downto 0);
+ constant INIT_DATA : data_arr_t := (
+ X"80", X"A8", X"80", X"3F",
+ X"80", X"D3", X"80", X"00",
+ X"80", X"40",
+ X"80", X"A0",
+ X"80", X"C0",
+ X"80", X"DA", X"80", X"02",
+ X"80", X"81", X"80", X"7F",
+ X"80", X"A4",
+ X"80", X"A6",
+ X"80", X"D5", X"80", X"80",
+ X"80", X"8D", X"80", X"14",
+ X"80", X"AF", -- init done
+ X"80", X"A5" --entire display on
+ );
+
+ signal rst_n : std_logic;
+ signal rst_sync : std_logic;
+
+ signal i2c_clk : std_logic;
+
+ signal master_start, master_stop, master_run : std_logic;
+ signal rw : std_logic;
+
+ signal tx_valid, tx_ready, tx_clear_buffer : std_logic;
+ signal tx_data : std_logic_vector(7 downto 0);
+
+ signal rx_valid, rx_confirm : std_logic;
+ signal rx_data : std_logic_vector(7 downto 0);
+
+ signal waiting : std_logic;
+
+ signal sda, scl : std_logic;
+ signal sda_enable, scl_enable : std_logic;
+
+ signal curr_index : natural;
+ signal next_index : natural;
+ signal go_next : std_logic;
+
+ type state_t is (IDLE, START, COUNT, STOP, DONE);
+ signal curr_state : state_t;
+ signal next_state : state_t;
+begin -- architecture a1
+ tx_clear_buffer <= '0';
+ master_run <= '1';
+ rst_n <= not rst_sync;
+ waiting_o <= waiting;
+ -- i2c_clk <= clk_i;
+
+ rw <= '0';
+
+ full_on_state_o <= std_logic_vector(to_unsigned(state_t'pos(curr_state), 3));
+
+
+ next_index <= 0 when curr_state = IDLE else
+ (curr_index + 1) when go_next = '1' else
+ curr_index;
+
+ go_next <= '1' when tx_ready = '1' and curr_index < INIT_DATA'length else '0';
+
+
+ tx_valid <= '1' when tx_ready = '1' and curr_index < INIT_DATA'length else '0';
+ tx_data <= INIT_DATA(curr_index) when curr_index < INIT_DATA'length else "00000000";
+
+ next_state <= START when start_i = '1' and curr_state = IDLE else
+ COUNT when curr_state = START else
+ STOP when curr_state = COUNT and curr_index = INIT_DATA'length and waiting = '1' else
+ DONE when curr_state = STOP else
+ IDLE when curr_state = DONE and start_i = '0' else
+ curr_state;
+
+ master_start <= '1' when curr_state = START else '0';
+ master_stop <= '1' when curr_state = STOP else '0';
+
+ divider: entity utils.clock_divider
+ generic map (
+ IN_FREQ => CLK_FREQ,
+ OUT_FREQ => I2C_CLK_FREQ)
+ port map (
+ clk_i => clk_i,
+ clk_o => i2c_clk);
+
+ i2c_master: entity i2c.master
+ generic map (
+ SCL_FALLING_DELAY => DELAY,
+ SCL_MIN_STABLE_CYCLES => SCL_MIN_STABLE_CYCLES)
+ port map (
+ clk_i => i2c_clk,
+ rst_in => rst_n,
+--
+ slave_address_i => ADDRESS,
+--
+ generate_ack_i => '1',
+ expect_ack_i => '1',
+--
+ rx_valid_o => rx_valid,
+ rx_data_o => rx_data,
+ rx_confirm_i => rx_confirm,
+--
+ tx_ready_o => tx_ready,
+ tx_valid_i => tx_valid,
+ tx_data_i => tx_data,
+ tx_clear_buffer_i => tx_clear_buffer,
+--
+ err_noack_data_o => err_noack_data_o,
+ err_noack_address_o => err_noack_address_o,
+ err_arbitration_o => err_arbitration_o,
+ err_general_o => err_general_o,
+--
+ stop_i => master_stop,
+ start_i => master_start,
+ run_i => master_run,
+ rw_i => rw,
+--
+ dev_busy_o => dev_busy_o,
+ bus_busy_o => bus_busy_o,
+ waiting_o => waiting,
+--
+ sda_i => sda,
+ scl_i => scl,
+ sda_enable_o => sda_enable,
+ scl_enable_o => scl_enable);
+
+ sync_reset: entity utils.metastability_filter
+ port map (
+ clk_i => clk_i,
+ signal_i => rst_i,
+ signal_o => rst_sync);
+
+ sda_open_buffer: entity utils.open_drain_buffer
+ port map (
+ pad_io => sda_io,
+ enable_i => sda_enable,
+ state_o => sda);
+ scl_open_buffer: entity utils.open_drain_buffer
+ port map (
+ pad_io => scl_io,
+ enable_i => scl_enable,
+ state_o => scl);
+
+ set_regs: process (i2c_clk) is
+ begin -- process set_regs
+ if rising_edge(i2c_clk) then -- rising clock edge
+ if rst_n = '0' then -- synchronous reset (active low)
+ curr_state <= IDLE;
+ curr_index <= 0;
+ else
+ curr_state <= next_state;
+ curr_index <= next_index;
+ end if;
+ end if;
+ end process set_regs;
+
+end architecture a1;
A src/ssd1306/ssd1306_pkg.vhd => src/ssd1306/ssd1306_pkg.vhd +134 -0
@@ 0,0 1,134 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+package ssd1306_pkg is
+
+ constant DISPLAY_WIDTH : unsigned(7 downto 0) := to_unsigned(128, 8);
+ constant DISPLAY_HEIGHT : unsigned(7 downto 0) := to_unsigned(64, 8);
+
+ type data_arr_t is array (natural range <>) of std_logic_vector(7 downto 0);
+
+ constant SSD1306_INIT : data_arr_t := (
+ X"80", X"A8", X"80", X"3F",
+ X"80", X"D3", X"80", X"00",
+ X"80", X"40",
+ X"80", X"A0",
+ X"80", X"C0",
+ X"80", X"DA", X"80", X"02",
+ X"80", X"81", X"80", X"7F",
+ X"80", X"A4",
+ X"80", X"A6",
+ X"80", X"D5", X"80", X"80",
+ X"80", X"8D", X"80", X"14",
+ X"80", X"AF" -- init done
+ );
+
+ constant SSD1306_HORIZONTAL_ADDRESSING_MODE : data_arr_t := (
+ X"80", X"20", -- set addressing mode
+ X"80", X"00" -- horizontal
+ );
+
+ constant SSD1306_CURSOR_TO_ZERO : data_arr_t := (
+ X"80", X"21", -- set column address
+ X"80", X"00", -- set first address
+ X"80", X"7F", -- set last address
+
+ X"80", X"22", -- set page address
+ X"80", X"00", -- start
+ X"80", X"07" -- end
+ );
+
+ constant SSD1306_ZERO_LEN : integer := 1025;
+ constant SSD1306_ZERO : data_arr_t := (
+ X"40", X"00"
+ );
+
+ -----------------------------------------------------------------------------
+ -- 8x8 characters obtained from https://github.com/dhepper/font8x8
+ -----------------------------------------------------------------------------
+
+ -- contains beginning of i2c packet
+ -- (0x40) to signal only data will follow
+
+constant SSD1306_CHAR_0 : data_arr_t(0 to 8) := (
+ X"40",
+ X"3E", X"7F", X"71", X"59",
+ X"4D", X"7F", X"3E", X"00"
+);
+constant SSD1306_CHAR_1 : data_arr_t(0 to 8) := (
+ X"40",
+ X"40", X"42", X"7F", X"7F",
+ X"40", X"40", X"00", X"00"
+);
+constant SSD1306_CHAR_2 : data_arr_t(0 to 8) := (
+ X"40",
+ X"62", X"73", X"59", X"49",
+ X"6F", X"66", X"00", X"00"
+);
+constant SSD1306_CHAR_3 : data_arr_t(0 to 8) := (
+ X"40",
+ X"22", X"63", X"49", X"49",
+ X"7F", X"36", X"00", X"00"
+);
+constant SSD1306_CHAR_4 : data_arr_t(0 to 8) := (
+ X"40",
+ X"18", X"1C", X"16", X"53",
+ X"7F", X"7F", X"50", X"00"
+);
+constant SSD1306_CHAR_5 : data_arr_t(0 to 8) := (
+ X"40",
+ X"27", X"67", X"45", X"45",
+ X"7D", X"39", X"00", X"00"
+);
+constant SSD1306_CHAR_6 : data_arr_t(0 to 8) := (
+ X"40",
+ X"3C", X"7E", X"4B", X"49",
+ X"79", X"30", X"00", X"00"
+);
+constant SSD1306_CHAR_7 : data_arr_t(0 to 8) := (
+ X"40",
+ X"03", X"03", X"71", X"79",
+ X"0F", X"07", X"00", X"00"
+);
+constant SSD1306_CHAR_8 : data_arr_t(0 to 8) := (
+ X"40",
+ X"36", X"7F", X"49", X"49",
+ X"7F", X"36", X"00", X"00"
+);
+constant SSD1306_CHAR_9 : data_arr_t(0 to 8) := (
+ X"40",
+ X"06", X"4F", X"49", X"69",
+ X"3F", X"1E", X"00", X"00"
+);
+
+ function ssd1306_bcd_digit_data (
+ constant digit : std_logic_vector(3 downto 0))
+ return data_arr_t;
+
+end package ssd1306_pkg;
+
+package body ssd1306_pkg is
+
+ function ssd1306_bcd_digit_data (
+ constant digit : std_logic_vector(3 downto 0))
+ return data_arr_t is
+ variable ret : data_arr_t(0 to 8);
+ begin
+ case digit is
+ when "0000" => ret := SSD1306_CHAR_0;
+ when "0001" => ret := SSD1306_CHAR_1;
+ when "0010" => ret := SSD1306_CHAR_2;
+ when "0011" => ret := SSD1306_CHAR_3;
+ when "0100" => ret := SSD1306_CHAR_4;
+ when "0101" => ret := SSD1306_CHAR_5;
+ when "0110" => ret := SSD1306_CHAR_6;
+ when "0111" => ret := SSD1306_CHAR_7;
+ when "1000" => ret := SSD1306_CHAR_8;
+ when "1001" => ret := SSD1306_CHAR_9;
+ when others => ret := SSD1306_CHAR_0;
+ end case;
+ return ret;
+ end function ssd1306_bcd_digit_data;
+
+end package body ssd1306_pkg;
A src/utils/bcd_counter.vhd => src/utils/bcd_counter.vhd +39 -0
@@ 0,0 1,39 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity bcd_counter is
+
+ generic (
+ MAX : integer := 10);
+
+ port (
+ clk_i : in std_logic;
+ rst_in : in std_logic;
+ carry_i : in std_logic;
+ carry_o : out std_logic;
+ count_o : out std_logic_vector(3 downto 0));
+
+end entity bcd_counter;
+
+architecture a1 of bcd_counter is
+ signal count_next : unsigned(3 downto 0);
+ signal count_reg : unsigned(3 downto 0);
+begin -- architecture a1
+
+ count_next <= ((count_reg + 1) mod MAX) when carry_i = '1' else count_reg;
+ carry_o <= '1' when count_next = "0000" and carry_i = '1' else '0';
+ count_o <= std_logic_vector(count_reg);
+
+ set_count: process (clk_i) is
+ begin -- process set_count
+ if rising_edge(clk_i) then -- rising clock edge
+ if rst_in = '0' then -- synchronous reset (active low)
+ count_reg <= "0000";
+ else
+ count_reg <= count_next;
+ end if;
+ end if;
+ end process set_count;
+
+end architecture a1;
A src/utils/clock_divider.vhd => src/utils/clock_divider.vhd +36 -0
@@ 0,0 1,36 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity clock_divider is
+
+ generic (
+ IN_FREQ : integer;
+ OUT_FREQ : integer);
+
+ port (
+ clk_i : in std_logic;
+ clk_o : out std_logic);
+
+end entity clock_divider;
+
+architecture a1 of clock_divider is
+ constant MAX : integer := IN_FREQ / OUT_FREQ;
+ signal counter_next : integer range 0 to MAX;
+ signal counter_reg : integer range 0 to MAX;
+begin -- architecture a1
+ keep_max_freq: if IN_FREQ = OUT_FREQ generate
+ clk_o <= clk_i;
+ end generate keep_max_freq;
+
+ counter: if IN_FREQ /= OUT_FREQ generate
+ counter_next <= (counter_reg + 1) when counter_reg < MAX else 0;
+ clk_o <= '1' when counter_reg >= MAX/2 else '0';
+ set_counter: process (clk_i) is
+ begin -- process set_counter
+ if rising_edge(clk_i) then -- rising clock edge
+ counter_reg <= counter_next;
+ end if;
+ end process set_counter;
+ end generate counter;
+
+end architecture a1;
A src/utils/counter.vhd => src/utils/counter.vhd +54 -0
@@ 0,0 1,54 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity counter is
+
+ generic (
+ MAX : natural := 9;
+ DIGITS : natural;
+ IN_FREQ : natural;
+ OUT_FREQ : natural);
+
+ port (
+ clk_i : in std_logic;
+ rst_in : in std_logic;
+ value_o : out std_logic_vector(DIGITS * 4 - 1 downto 0));
+
+end entity counter;
+
+architecture a1 of counter is
+ signal count_clk : std_logic;
+
+ signal carries : std_logic_vector(DIGITS downto 0);
+ signal value : std_logic_vector(DIGITS * 4 - 1 downto 0);
+begin -- architecture a1
+ value_o <= value;
+
+ pulse_gen: entity work.sync_edge_detector
+ port map (
+ clk_i => clk_i,
+ signal_i => count_clk,
+ rising_edge_o => carries(0),
+ falling_edge_o => open);
+
+ clock_divider: entity work.clock_divider
+ generic map (
+ IN_FREQ => IN_FREQ,
+ OUT_FREQ => OUT_FREQ)
+ port map (
+ clk_i => clk_i,
+ clk_o => count_clk);
+
+ counters: for i in 0 to DIGITS - 1 generate
+ bcd_counter: entity work.bcd_counter
+ generic map (
+ MAX => MAX)
+ port map (
+ clk_i => clk_i,
+ rst_in => rst_in,
+ carry_i => carries(i),
+ carry_o => carries(i + 1),
+ count_o => value((i + 1) * 4 - 1 downto i * 4));
+ end generate counters;
+
+end architecture a1;
A tb/ssd1306/counter_tb.vhd => tb/ssd1306/counter_tb.vhd +96 -0
@@ 0,0 1,96 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library vunit_lib;
+context vunit_lib.vunit_context;
+
+library ssd1306;
+
+library utils;
+
+library i2c_tb;
+use i2c_tb.tb_pkg.all;
+use i2c_tb.tb_i2c_pkg.all;
+use i2c_tb.tb_i2c_slave_pkg.all;
+
+entity counter_tb is
+
+ generic (
+ runner_cfg : string);
+
+end entity counter_tb;
+
+architecture tb of counter_tb is
+ constant ADDRESS : std_logic_vector(6 downto 0) := "0111100";
+ constant CLK_PERIOD : time := 10 ns;
+
+ signal rst_n : std_logic := '0';
+ signal rst : std_logic;
+
+ signal not_scl : std_logic;
+
+ signal err_noack_data, err_noack_address, err_arbitration, err_general : std_logic;
+ signal bus_busy, dev_busy : std_logic;
+
+ signal one : std_logic := '1';
+ constant SCL_MIN_STABLE_CYCLES : natural := 10;
+ constant TIMEOUT : time := SCL_MIN_STABLE_CYCLES * CLK_PERIOD * 4;
+begin -- architecture tb
+ uut : entity ssd1306.ssd1306_counter
+ generic map (
+ CLK_FREQ => 100,
+ I2C_CLK_FREQ => 10,
+ COUNT_FREQ => 5,
+ DELAY => 1,
+ EXPECT_ACK => '0',
+ SCL_MIN_STABLE_CYCLES => SCL_MIN_STABLE_CYCLES)
+ port map (
+ clk_i => clk,
+ rst_i => rst,
+ start_i => '1',
+ err_noack_data_o => err_noack_data,
+ err_noack_address_o => err_noack_address,
+ err_arbitration_o => err_arbitration,
+ err_general_o => err_general,
+ dev_busy_o => dev_busy,
+ bus_busy_o => bus_busy,
+ sda_io => sda,
+ scl_io => scl
+ );
+
+ sda <= 'H';
+ scl <= 'H';
+
+ not_scl <= not scl;
+
+ clk <= not clk after CLK_PERIOD / 2;
+ rst_n <= '1' after 6 * CLK_PERIOD;
+ rst <= not rst_n;
+
+ -- TODO: allow conditions from master...
+ -- sda_stability_check: check_stable(clk, one, scl, not_scl, sda);
+
+ main: process is
+ begin -- process main
+ wait until rst_n = '1';
+ wait for 2 * CLK_PERIOD;
+ wait until falling_edge(clk);
+ test_runner_setup(runner, runner_cfg);
+
+ while test_suite loop
+ if run("no_check") then
+ wait for 50 ms;
+ -- i2c_slave_check_start(ADDRESS, '0', TIMEOUT, scl, sda);
+ -- for i in 0 to 100 loop
+ -- i2c_slave_receive(X"ZZ");
+ -- end loop; -- i
+ -- -- stop because no ack
+ -- i2c_slave_check_stop(TIMEOUT * 10, scl, sda);
+ end if;
+ end loop;
+
+ test_runner_cleanup(runner);
+ end process main;
+
+end architecture tb;
A tb/ssd1306/full_on_tb.vhd => tb/ssd1306/full_on_tb.vhd +110 -0
@@ 0,0 1,110 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library vunit_lib;
+context vunit_lib.vunit_context;
+
+library ssd1306;
+
+library utils;
+
+library i2c_tb;
+use i2c_tb.tb_pkg.all;
+use i2c_tb.tb_i2c_pkg.all;
+use i2c_tb.tb_i2c_slave_pkg.all;
+
+entity full_on_tb is
+
+ generic (
+ runner_cfg : string);
+
+end entity full_on_tb;
+
+architecture tb of full_on_tb is
+ constant ADDRESS : std_logic_vector(6 downto 0) := "0111100";
+ constant CLK_PERIOD : time := 10 ns;
+
+ signal rst_n : std_logic := '0';
+ signal rst : std_logic;
+
+ signal not_scl : std_logic;
+
+ signal err_noack_data, err_noack_address, err_arbitration, err_general : std_logic;
+ signal bus_busy, dev_busy : std_logic;
+
+ signal one : std_logic := '1';
+ constant SCL_MIN_STABLE_CYCLES : natural := 10;
+ constant TIMEOUT : time := SCL_MIN_STABLE_CYCLES * CLK_PERIOD * 4;
+begin -- architecture tb
+ uut : entity ssd1306.full_on
+ generic map (
+ CLK_FREQ => 10000000,
+ I2C_CLK_FREQ => 10000000,
+ DELAY => 1,
+ SCL_MIN_STABLE_CYCLES => SCL_MIN_STABLE_CYCLES)
+ port map (
+ clk_i => clk,
+ rst_i => rst,
+ start_i => '1',
+ err_noack_data_o => err_noack_data,
+ err_noack_address_o => err_noack_address,
+ err_arbitration_o => err_arbitration,
+ err_general_o => err_general,
+ dev_busy_o => dev_busy,
+ bus_busy_o => bus_busy,
+ sda_io => sda,
+ scl_io => scl
+ );
+
+ sda <= 'H';
+ scl <= 'H';
+
+ not_scl <= not scl;
+
+ clk <= not clk after CLK_PERIOD / 2;
+ rst_n <= '1' after 6 * CLK_PERIOD;
+ rst <= not rst_n;
+
+ -- TODO: allow conditions from master...
+ -- sda_stability_check: check_stable(clk, one, scl, not_scl, sda);
+
+ main: process is
+ begin -- process main
+ wait until rst_n = '1';
+ wait for 2 * CLK_PERIOD;
+ wait until falling_edge(clk);
+ test_runner_setup(runner, runner_cfg);
+
+ while test_suite loop
+ if run("start") then
+ i2c_slave_check_start(ADDRESS, '0', TIMEOUT, scl, sda);
+ i2c_slave_receive(X"80", TIMEOUT, scl, sda);
+ i2c_slave_receive(X"A8", TIMEOUT, scl, sda);
+ i2c_slave_receive(X"80", TIMEOUT, scl, sda);
+ i2c_slave_receive(X"3F", TIMEOUT, scl, sda);
+ i2c_slave_receive(X"80", TIMEOUT, scl, sda);
+ -- stop because no ack
+ i2c_slave_check_stop(TIMEOUT * 10, scl, sda);
+ elsif run("last") then
+ i2c_slave_check_start(ADDRESS, '0', TIMEOUT, scl, sda);
+ for i in 0 to 35 loop
+ i2c_slave_receive(X"ZZ", TIMEOUT, scl, sda);
+ end loop; -- i
+ i2c_slave_receive(X"80", TIMEOUT, scl, sda);
+ i2c_slave_receive(X"A5", TIMEOUT, scl, sda);
+ i2c_slave_check_stop(TIMEOUT, scl, sda);
+ elsif run("every_second_X80") then
+ i2c_slave_check_start(ADDRESS, '0', TIMEOUT, scl, sda);
+ for i in 0 to 18 loop
+ i2c_slave_receive(X"80", TIMEOUT, scl, sda);
+ i2c_slave_receive(X"ZZ", TIMEOUT, scl, sda);
+ end loop; -- i
+ i2c_slave_check_stop(TIMEOUT, scl, sda);
+ end if;
+ end loop;
+
+ test_runner_cleanup(runner);
+ end process main;
+
+end architecture tb;