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

Previous Meeting: Wednesday September 7, 2016

Topic revision: r5 - 2020-02-17 - 15:36:18 - JimLewis
 
Copyright © 2008-2021 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback