P1076 September 15, 2016 Meeting Minutes
Attendees:
- Brent Hayhoe, Patrick Lehmann, Rob Gaddi, Jim Lewis, Lieven Lemiengre, Kevin Jennings, Peter Flake, Jing Pang
Agenda:
Meeting Discussion
- Interfaces - continue.
- Reviewed ahb lite example with space ship operators from Rob
- Reviewed spi example from Lieven with map functions
- Actions / Next Steps
- Test more syntax using popular editor
-------------------------------------------
-- Support Package for AHB-Lite Protocol --
-------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
package amba_ahbl_pkg
subtype HADDR_vst is std_logic_vector(31 downto 0);
subtype HBURST_vst is std_logic_vector(3 downto 0);
subtype HPROT_vst is std_logic_vector(3 downto 0);
subtype HSIZE_vst is std_logic_vector(2 downto 0);
subtype HTRANS_vst is std_logic_vector(1 downto 0);
constant HTRANS_IDLE : HTRANS_vst := "00";
constant HTRANS_BUSY : HTRANS_vst := "01";
constant HTRANS_NONSEQ : HTRANS_vst := "10";
constant HTRANS_SEQ : HTRANS_vst := "11";
constant HBURST_SINGLE : HBURST_vst := "000"; -- Single burst
constant HBURST_INCR : HBURST_vst := "001"; -- Incrementing burst of undefined length
constant HBURST_WRAP4 : HBURST_vst := "010"; -- 4-beat wrapping burst
constant HBURST_INCR4 : HBURST_vst := "011"; -- 4-beat incrementing burst
constant HBURST_WRAP8 : HBURST_vst := "100"; -- 8-beat wrapping burst
constant HBURST_INCR8 : HBURST_vst := "101"; -- 8-beat incrementing burst
constant HBURST_WRAP16 : HBURST_vst := "110"; -- 16-beat wrapping burst
constant HBURST_INCR16 : HBURST_vst := "111"; -- 16-beat incrementing burst
-----------------------------------------------------
-- Record & Array Types for AMBA AHB-Lite Protocol --
-----------------------------------------------------
-- The read/write data vectors are of undefined size.
type ahbl_globals is record
HCLK : std_logic;
HRESET : std_logic;
end record ahbl_globals;
type ahbl_mosi_data is record
HADDR_vst : HADDR_vst; -- AHBL address bus (32b)
HBURST : HBURST_vst; -- AHBL burst type
HMASTLOCK : std_logic; -- AHBL locked transfer control
HPROT : HPROT_vst; -- AHBL access protection control
HSIZE : HSIZE_vst; -- AHBL transfer size control
HTRANS : HTRANS_vst; -- AHBL transfer type control
HWDATA : std_logic_vector; -- AHBL write data bus (min 32 bits)
HWRITE : std_logic -- AHBL transfer write/read control
end record ahbl_mosi_data;
type ahbl_miso_data is record
HRDATA : std_logic_vector; -- AHBL read data bus (min 32 bits)
HREADY : std_logic; -- AHBL transfer complete
HRESP : std_logic -- AHBL transfer response
end record ahbl_miso_data;
type ahbl_master is port record
mosi : out ahbl_mosi_data;
miso : in ahbl_miso_data;
gbl : in ahbl_globals
end port record ahbl_master;
type ahbl_slave is port record
mosi : in ahbl_mosi_data;
miso : out ahbl_miso_data;
HSEL : in std_logic;
gbl : in ahbl_globals
end port record ahbl_master;
type ahbl_miso_arr is array(natural range <>) of ahbl_miso_data;
subtype ahbl_mosi_data32 is ahbl_mosi_data(HWDATA(31 downto 0));
subtype ahbl_miso_data32 is ahbl_miso_data(HRDATA(31 downto 0));
subtype ahbl_miso_arr32 is array(natural range <>) of ahbl_miso_data32;
-- The proper syntax is one of these things here ----------------\
subtype ahbl_master32 is ahbl_master(
mosi(ahbl_mosi_data32), miso(ahbl_miso_data32) -- *
);
subtype ahbl_master32 is ahbl_master(
mosi(HWDATA(31 downto 0)), miso(HWDATA(31 downto 0)) -- *
);
-- Constraining record elements LRM p.52(66)
-- signal sig : record_t(elem1(0 to 7), elem2(0 to 3));
subtype ahbl_slave32 is ahbl_master(
mosi(ahbl_mosi_data32), miso(ahbl_miso_data32)
);
--------------------------------
-- Context Clause for Package --
--------------------------------
context amba_ahbl_context;
library ieee;
use ieee.std_logic.1164.all;
use work.amba_ahbl_pkg.all;
end context amba_ahbl_context;
end package amba_ahbl_pkg;
package body amba_ahbl_pkg;
end package body amba_ahbl_pkg;
-----------------------------------------------------------------------
-- Declare entities for the master, slave and interconnect
-----------------------------------------------------------------------
-- Wrap the decoder and multiplexer into a single interconnect block.
context amba_ahbl_context;
entity ahbl_interconnect is
generic (
NSLAVES : positive
);
port (
HADDR : in std_logic_vector;
HSEL : out std_logic_vector(NSLAVES-1 downto 0);
slv : in ahbl_miso_arr(NSLAVES-1 downto 0);
mst : out ahbl_miso_data
);
end entity ahbl_interconnect;
context amba_ahbl_context;
entity ahbl_cpu_haahvahd is
port (
inst : port record ahbl_master32;
data : port record ahbl_master32
);
end entity ahbl_cpu_haahvahd;
context amba_ahbl_context;
entity ahbl_ram is
generic (
ADDR_BITS : positive
);
port (
iface : port record ahbl_slave32;
);
end entity ahbl_ram;
-----------------------------------------------------------------------
-- Top Level
-----------------------------------------------------------------------
context work.amba_ahbl_context;
entity ahbl_block_ent is(
end entity ahbl_block_ent;
architecture rtl_arch of ahbl_block_ent is
signal m_amba_ahb_rs : m_amba_ahb_rt;
signal HSEL_vs : HSEL_vst;
signal s_amba_ahb_rs : s_amba_ahb_rt;
signal s_amba_ahb_as : s_amba_ahb_at;
signal gbl : ahbl_globals;
signal inst_bus : ahbl_master32'record;
signal data_bus : ahbl_master32'record;
signal slave_responses : ahbl_miso_arr32(2 downto 0);
signal HSEL : std_logic_vector(2 downto 0);
begin
-- Clock and Reset Control for AHB-Lite Block
clk_and_rst_inst : entity clk_and_rst_ent(rtl_arch)
port map(
rst_o => gbl.HRESET
clk_o => gbl.HCLK
);
gbl <=> inst_bus.gbl;
gbl <=> data_bus.gbl;
CPU: entity work.ahbl_cpu_haahvahd
port map (
inst => inst_bus,
data => data_bus
);
RAM_SLAVES: for i in 0 to 2 generate
signal slv : ahbl_slave32'record;
begin
slv.mosi <=> data_bus.mosi;
slv.gbl <=> gbl;
slv.miso <=> slave_responses(i);
slv.HSEL <=> HSEL(i);
RAM: entity work.entity ahbl_ram is
generic map (
ADDR_BITS => 12
)
port map (
iface => slv
);
end generate;
IC: entity work.ahbl_interconnect
generic map (
NSLAVES => 2
) port map (
HADDR => data_bus.mosi.HADDR,
HSEL => HSEL,
slv => slave_responses,
mst => data_bus.miso
);
end architecture rtl_arch;
type handshake_r is record
rdy : std_logic;
ack : std_logic;
end type;
port view handshake_mst of handshake_r is
rdy : out
ack : in
end port view;
-- handshake_mst'subtype refers to handshake_r
type write_address_channel is port record
addr : out std_logic vector;
lock : out std_logic;
hs : port record handshake;
end type;
type write_data_channel is port record
dout : out std_logic_vector;
hs : port record handshake
end type;
type handshake is port record
rdy : out std_logic;
ack : in std_logic;
end type;
type write_address_channel is port record
addr : out std_logic vector;
lock : out std_logic;
hs : port record handshake;
end type;
type write_data_channel is port record
dout : out std_logic_vector;
hs : port record handshake
end type;
type read_address_channel is port record
-- ...
end type;
type read_data_channel is port record
-- ...
end type;
type axi is port record
write_adr : port record write_address_channel;
write_data : port record write_data_channel;
read_adr : port record read_address_channel;
read_data : port record read_data_channel;
end type;
From Lieven's Kobra.io page:
package spi_bus_pkg is
-- Underlying types. Masters use an unconstrained vector for
-- chip selects; actual instantiations of the master interface
-- will have to deal with making these concrete.
type spi_master_r is record
mosi : std_logic; -- Data from master to slave
miso : std_logic; -- Data from slave to master
sclk : std_logic; -- Serial clock
ssel : std_logic_vector -- Chip selects (active-low)
end record spi_master_r;
type spi_slave_r is record
mosi : std_logic; -- Data from master to slave
miso : std_logic; -- Data from slave to master
sclk : std_logic; -- Serial clock
ssel : std_logic; -- Chip select (active-low)
end record spi_slave_r;
port view master of spi_master_r is
mosi => out;
miso => in;
sclk => out;
ssel => out;
end port view master;
-- automatically generated?
map function master_constructor(signal mst : spi_master_r) to bus master spi_slave_r is
begin
map (
mosi => mst.mosi,
miso => mst.miso,
sclk => mst.sclk,
ssel => mst.ssel
);
end map function;
alias reverse_master is master'reversed;
port view slave of spi_slave_r is
mosi => in;
miso => out;
sclk => in;
ssel => in;
end port view slave;
alias reverse_slave is slave'reversed;
-- map functions are structural functions, regular functions are behavioural
map function slave_select(mst : bus reverse_master spi_master_r; chip_select : natural) to bus reverse_slave spi_slave_r
is
-- constant foo : integer := chip_select * 2;
-- alias xys is mst.foo(2).bar;
begin
return map (
mosi => mst.mosi,
miso => mst.miso,
sclk => mst.sclk,
ssel => mst.ssel(chip_select)
);
end map function;
map function clk_extractor(signal WB_SYS : in t_wb_sys; signal o : out std_logic) to (signal a : std_logic)
is
begin
return WB_SYS.CLK_I; -- ??
map WB_SYS.CLK_I; -- ??
end map function;
-- entity clk_extractor is
-- port ( WB_SYS : in t_wb_sys; o : out std_logic);
-- end entity clk_extractor;
-- architecture Behavioral of clk_extractor is
-- begin
-- o <=> WB_SYS.CLK_I;
-- end architecture Behavioral;
end package spi_bus_pkg;
package body spi_bus_pkg is
end package body spi_bus_pkg;
------------------------------------------------------------------------
-- Using entities rather than mapfunctions to connect masters/slaves
-- to the physical wiring on the PCB, since they exist right now and
-- mapfunctions don't.
--
-- The use of the spaceship operator (<=>) below is intended to make
-- these connections without introducing delta cycles.
------------------------------------------------------------------------
-- Connect an SPI slave to an SPI bus. Since the bus is of type
-- spi_master_r, but drives into us, we need to use the reverse of a
-- master interface (or an anti-master) on this entity, and an
-- anti-slave on the other side to connect to the slave.
--
-- FPGA spi_slave_link ADC
-- ---------- -------------------- ---------
-- Master |-------| Anti- Anti- |----| Slave |
-- | | Master Slave | | |
-- ---------- -------------------- ---------
--
use work.spi_bus_pkg.all;
-- entity spi_slave_link is
-- generic (
-- CHIP_SELECT : natural
-- );
-- port (
-- mst : bus spi_master_r(master)'reversed;
-- slv : bus spi_slave_r(slave)'reversed;
-- );
-- end entity spi_slave_link;
--
-- architecture Behavioral of spi_slave_link is
-- begin
-- slv.mosi <=> mst.mosi; -- <----
-- slv.miso <=> mst.miso; -- ---->
-- slv.sclk <=> mst.sclk; -- <----
-- slv.ssel <=> mst.ssel(CHIP_SELECT); -- <----
-- end architecture Behavioral;
-- This can be used to create our synthesizable code:
-- Read 16 bits at a time from 3 ADCs, present them broadside.
-- Write 16 bits to 1 DAC.
-- The spi port declaration below is the only place that the number
-- of chip selects is ever defined; everything else inherits that from
-- this single point. SSEL(2 downto 0) are ADCs, SSEL(3) is the DAC.
--
use work.spi_bus_pkg.all;
entity spi_communicator is
port (
spi : bus master spi_master_r(ssel(3 downto 0));
dac : in std_logic_vector(15 downto 0);
adc0 : out std_logic_vector(15 downto 0);
adc1 : out std_logic_vector(15 downto 0);
adc2 : out std_logic_vector(15 downto 0);
clk : in std_logic;
rst : in std_logic
);
end entity spi_communicator;
architecture Behavioral of spi_communicator is
begin
MACHINE: process
variable data : std_logic_vector(15 downto 0);
variable bitn : integer range data'range;
variable dev : integer range spi_ssel'range;
type state is (RESET, SSEL_GO, SCLK_FALL, SCLK_RISE, SSEL_STOP);
begin
wait until rising_edge(clk);
case state is
when RESET =>
spi.mosi <= 'U';
spi.ssel <= (others => '1');
spi.sclk <= '1';
adc0 <= (others => 'U');
adc1 <= (others => 'U');
adc2 <= (others => 'U');
state := SSEL_GO;
dev := 0;
when SSEL_GO =>
spi.ssel(dev) <= '0';
state := SCLK_FALL;
bitn := data'high;
when SCLK_FALL =>
spi.sclk <= '0';
state := SCLK_RISE;
when SCLK_RISE =>
spi.sclk <= '1';
spi.mosi <= dac(bitn);
data(bitn) := spi.miso;
if bitn = 0 then
state := SSEL_STOP;
else
state := SCLK_FALL;
bitn := bitn - 1;
end if;
when SSEL_STOP =>
spi.ssel <= (others => '1');
case adc is
when 0 => adc0 <= data;
when 1 => adc1 <= data;
when 2 => adc2 <= data;
when 3 => null;
end case;
adc := 0 when adc = 3 else (adc + 1);
state := SSEL_GO;
end case;
if rst then
state := RESET;
end if;
end process MACHINE;
end architecture spi_communicator;
----------------------------------------------------------------------
-- Wrap it in an FPGA that does, presumably, other things as well.
use work.spi_bus_pkg.all;
entity FPGA is
port(
-- Number of chip selects here is undefined; the spi_communicator is
-- sufficient to provide a definite size.
spi : bus master spi_master_r;
-- ...
clk : in std_logic;
rst : in std_logic
);
end entity FPGA
architecture Structural of FPGA is
-- ...
begin
SPIMST: entity work.spi_communicator
port map (
spi => spi,
adc0 => intl_adc0,
adc1 => intl_adc1,
adc2 => intl_adc2,
dac => dac,
clk => clk,
rst => rst
);
-- ...
end architecture Structural;
And a testbench around it:
----------------------------------------------------------------------
-- Simulation models of ADC/DAC
use ieee.numeric_std.all;
use work.spi_bus_pkg.all;
entity adc is
port (
spi : bus slave spi_slave_r;
vin : in real;
);
end entity adc;
architecture Behavioral of adc is
signal intl_miso : std_logic_vector := 'Z';
begin
FAKEIT: process
variable data : unsigned(15 downto 0);
variable bitn : integer range data'high downto -1;
begin
intl_miso <= 'Z';
wait until falling_edge(spi.ssel);
data := TO_UNSIGNED(vin / 5.0 * 65536, data'length);
bitn := data'high;
loop
if bitn = -1 then
intl_miso <= 'U';
else
intl_miso <= data(bitn);
bitn := bitn - 1;
end if;
wait until rising_edge(spi.sclk) or rising_edge(spi.ssel);
exit when spi.ssel'event;
end loop;
end process FAKEIT;
spi.miso <= intl_miso after 10 ns;
end architecture Behavioral;
use ieee.numeric_std.all;
use work.spi_bus_pkg.all;
entity dac is
port (
spi : bus spi_slave_r(slave);
vout : out real := 0.0;
);
end entity dac;
architecture Behavioral of dac is
begin
FAKEIT: process
variable data : unsigned(15 downto 0);
variable bitn : integer range data'high downto -1;
begin
spi.miso <= 'Z';
wait until falling_edge(spi.ssel);
data := (others => '0');
loop
wait until rising_edge(spi.sclk) or rising_edge(spi.ssel);
exit when spi.ssel'event;
data := data(14 downto 0) & spi.mosi;
end loop;
vout <= REAL(TO_INTEGER(data)) * 5.0 / 65536.0 after 10 ns;
end process FAKEIT;
end architecture Behavioral;
----------------------------------------------------------------------
-- And the complete testbench
use work.spi_bus_pkg.all;
entity Testbench
end entity Testbench;
architecture TB of Testbench is
signal clk : std_logic;
signal rst : std_logic;
-- Notice that SPI is just a normal signal here; the top level has no
-- interest in the whole interfaces concept. Size is defined by the FPGA,
-- which gets it from the spi_communicator.
signal spi : spi_master_r;
port spi_bus : bus master spi_master_r => master_constructor(spi);
-- alternative
port spi_bus : bus master spi_master_r => (
mosi => mst.mosi,
miso => mst.miso,
sclk => mst.sclk,
ssel => mst.ssel
);
port clk2 : std_logic => clk; -- behaves like an alias
port daclnk : spi_slave_r => slave_select(spi_bus, 3);
begin
DUT: entity work.FPGA
port map (
clk => clk,
rst => rst,
spi => master_constructor(spi)
);
ADCS: for i in 0 to 2 generate
begin
-- And hook the ADC to that slave bus.
ADC: entity work.adc
port map (
spi => slave_select(spi, i),
vin => analog_voltage(i)
);
end generate ADCs;
DAC: entity work.dac
port map (
spi => slave_select(spi, 3),
vout => driven_voltage
);
-- And a resistive pullup for the tri-state MISO line.
spi.miso <= 'H';
end architecture TB;
Review and Approve Meeting Minutes and Decisions by Attendees
Review and Approve Meeting Minutes and Decisions by non-attendees
TBD
Next Meeting: Thursday September 22, 2016, 11 am Pacific