Skip to content

Commit

Permalink
Adds the transaction and physical layers of an I2C controller (#253)
Browse files Browse the repository at this point in the history
This is some early work on the lower layers of an I2C controller block.
I'm looking to get this reviewed and integrated a bit prematurely since
Nathanael is looking at building out an I2C target block and we'd like
to avoid stepping on one another's toes. Given all of this has not been
integrated into a design yet and is simulation only, everything is up
for grabs if we'd like to make changes. I've begun work on an
`i2c_core.vhd` (should probably be renamed `i2c_controller_top.vhd` or
something) that is going to attempt to link up a register control
interface to the low-level blocks. It would present read/write data
buffers as a streaming interface that we could hook up to FIFOs/RAMs/etc
as we see fit.
  • Loading branch information
Aaron-Hartwig authored Dec 11, 2024
1 parent 8942912 commit 6e2c16a
Show file tree
Hide file tree
Showing 15 changed files with 2,065 additions and 0 deletions.
19 changes: 19 additions & 0 deletions hdl/ip/vhd/i2c/common/BUCK
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("//tools:hdl.bzl", "vhdl_unit", "vunit_sim")

vhdl_unit(
name = "i2c_common_pkg",
srcs = ["i2c_common_pkg.vhd"],
visibility = ['PUBLIC']
)

vhdl_unit(
name = "i2c_cmd_vc",
srcs = ["sims/i2c_cmd_vc.vhd", "sims/i2c_cmd_vc_pkg.vhd"],
visibility = ['PUBLIC']
)

vhdl_unit(
name = "i2c_target_vc",
srcs = ["sims/i2c_target_vc.vhd", "sims/i2c_target_vc_pkg.vhd"],
visibility = ['PUBLIC']
)
91 changes: 91 additions & 0 deletions hdl/ip/vhd/i2c/common/i2c_common_pkg.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- Copyright 2024 Oxide Computer Company

library ieee;
use ieee.std_logic_1164.all;

package i2c_common_pkg is

--
-- Link Layer
--

type mode_t is (
STANDARD, -- up to 100 Kbps
FAST, -- up to 400 Kbps
FAST_PLUS -- up to 1 Mbps
);

-- A group of settings for generics to generate constants
-- all times in nanoseconds (ns)
type settings_t is record
fscl_period_ns : positive; -- SCL clock period
sda_su_ns : positive; -- data set-up time
sta_su_hd_ns : positive; -- START set-up/hold time
sto_su_ns : positive; -- STOP set-up time
sto_sta_buf_ns : positive; -- bus free time between STOP and START
end record;

function get_i2c_settings (constant mode : mode_t) return settings_t;

--
-- Transaction Layer
--

type op_t is (
READ,
WRITE,
-- RANDOM_READ will write an address byte and one more byte (intended to set an internal
-- address register on a peripheral) before issuing a repeated start for a read.
RANDOM_READ
);

type cmd_t is record
op : op_t;
addr : std_logic_vector(6 downto 0);
reg : std_logic_vector(7 downto 0);
len : std_logic_vector(7 downto 0);
end record;
constant CMD_RESET : cmd_t := (READ, (others => '0'), (others => '0'), (others => '0'));

end package;

package body i2c_common_pkg is

function get_i2c_settings (constant mode : mode_t) return settings_t is
variable r : settings_t;
begin
case mode is
when STANDARD =>
r := (
10_000, -- 10^9 / 100_000Hz
250,
4700,
4000,
4700
);
when FAST =>
r := (
2500, -- 10^9 / 400_000Hz
100,
600,
600,
1300
);
when FAST_PLUS =>
r := (
1000, -- 10^9 / 1_000_000Hz
50,
260,
260,
500
);
end case;

return r;
end;

end package body;
67 changes: 67 additions & 0 deletions hdl/ip/vhd/i2c/common/sims/i2c_cmd_vc.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- Copyright 2024 Oxide Computer Company

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.com_context;
use vunit_lib.sync_pkg.all;

use work.i2c_common_pkg.all;
use work.i2c_cmd_vc_pkg.all;

entity i2c_cmd_vc is
generic (
i2c_cmd_vc : i2c_cmd_vc_t
);
port (
cmd : out cmd_t := CMD_RESET;
valid : out std_logic := '0';
ready : in std_logic;
);
end entity;

architecture model of i2c_cmd_vc is
begin

handle_messages: process
variable msg : msg_t;
variable msg_type : msg_type_t;
variable command : cmd_t;
variable is_read : boolean;
variable is_random : boolean;
begin
receive(net, i2c_cmd_vc.p_actor, msg);
msg_type := message_type(msg);

if msg_type = push_i2c_cmd_msg then
is_read := pop(msg);
is_random := pop(msg);
if is_read then
if is_random then
command.op := RANDOM_READ;
else
command.op := READ;
end if;
else
command.op := WRITE;
end if;
command.addr := pop(msg);
command.reg := pop(msg);
command.len := pop(msg);
cmd <= command;
valid <= '1';
wait until ready;
valid <= '0';
else
unexpected_msg_type(msg_type);
end if;
end process;

end architecture;
86 changes: 86 additions & 0 deletions hdl/ip/vhd/i2c/common/sims/i2c_cmd_vc_pkg.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- Copyright 2024 Oxide Computer Company

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.numeric_std_unsigned.all;

library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.com_context;
context vunit_lib.vc_context;
use vunit_lib.sync_pkg.all;

use work.i2c_common_pkg.all;

package i2c_cmd_vc_pkg is

type i2c_cmd_vc_t is record
-- private
p_actor : actor_t;
p_logger : logger_t;
end record;

constant i2c_cmd_vc_logger : logger_t := get_logger("work:i2c_cmd_vc_pkg");

impure function new_i2c_cmd_vc(
actor : actor_t := null_actor;
logger : logger_t := i2c_cmd_vc_logger;
) return i2c_cmd_vc_t;

constant push_i2c_cmd_msg : msg_type_t := new_msg_type("push_i2c_cmd");

procedure push_i2c_cmd(
signal net : inout network_t;
i2c_cmd_vc : i2c_cmd_vc_t;
cmd : cmd_t;
);

end package;

package body i2c_cmd_vc_pkg is

impure function new_i2c_cmd_vc(
actor : actor_t := null_actor;
logger : logger_t := i2c_cmd_vc_logger;
) return i2c_cmd_vc_t is
variable p_actor : actor_t;
begin
p_actor := actor when actor /= null_actor else new_actor;

return (
p_actor => p_actor,
p_logger => logger
);
end function;

procedure push_i2c_cmd(
signal net : inout network_t;
i2c_cmd_vc : i2c_cmd_vc_t;
cmd : cmd_t
) is
variable msg : msg_t := new_msg(push_i2c_cmd_msg);
variable is_read : boolean;
variable is_random : boolean;
begin
-- breaking down our type since we can't push enums in VUnit
is_read := false when cmd.op = WRITE else true;
is_random := true when cmd.op = RANDOM_READ else false;

-- We break down cmd_t into it's primitive types to push them into the message where we will
-- pop them off when we receive it in order to reconstruct a cmd_t.
-- TODO: implement push/pop for our custom type so we wouldn't have to do this?
push(msg, is_read); -- boolean
push(msg, is_random); -- boolean
push(msg, cmd.addr); -- std_logic_vector
push(msg, cmd.reg); -- std_logic_vector
push(msg, cmd.len); -- std_logic_vector
send(net, i2c_cmd_vc.p_actor, msg);
end;


end package body;
Loading

0 comments on commit 6e2c16a

Please sign in to comment.