Import the JTAG interface code as used for the Chips4Maker pilot Retro-uC
authorStaf Verhaegen <staf@stafverhaegen.be>
Sun, 27 Aug 2017 20:05:24 +0000 (22:05 +0200)
committerStaf Verhaegen <staf@stafverhaegen.be>
Sun, 27 Aug 2017 20:05:24 +0000 (22:05 +0200)
This code has currently been tested in FPGA through a buspirate so should
already be functional.

20 files changed:
.gitignore [new file with mode: 0644]
bench/vhdl/idcode.vhdl [new file with mode: 0644]
bench/vhdl/sampleshift.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_idblock.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_ioblock.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_iocell.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_irblock.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_pkg.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_tap_controller.vhdl [new file with mode: 0644]
rtl/vhdl/c4m_jtag_tap_fsm.vhdl [new file with mode: 0644]
sim/cocotb/c4m_jtag.py [new file with mode: 0644]
sim/cocotb/controller/Makefile [new file with mode: 0644]
sim/cocotb/controller/c4m_jtag.py [new symlink]
sim/cocotb/controller/test.py [new file with mode: 0644]
sim/cocotb/dual_parallel/Makefile [new file with mode: 0644]
sim/cocotb/dual_parallel/c4m_jtag.py [new symlink]
sim/cocotb/dual_parallel/dual_parallel.vhdl [new file with mode: 0644]
sim/cocotb/dual_parallel/test.py [new file with mode: 0644]
sim/cocotb/dual_serial/TODO [new file with mode: 0644]
sim/ghdl/bench_idcode.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d703adb
--- /dev/null
@@ -0,0 +1,7 @@
+*.o
+*work-*.cf
+*~
+*.pyc
+results.xml
+*.ghw
+sim/ghdl/bench_idcode
diff --git a/bench/vhdl/idcode.vhdl b/bench/vhdl/idcode.vhdl
new file mode 100644 (file)
index 0000000..d9c718f
--- /dev/null
@@ -0,0 +1,99 @@
+-- reset JTAG interface and then IDCODE should be shifted out
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity bench_idcode is
+end bench_idcode;
+
+architecture rtl of bench_idcode is
+  signal TCK:   std_logic;
+  signal TMS:   std_logic;
+  signal TDI:   std_logic;
+  signal TDO:   std_logic;
+  signal TRST_N: std_logic;
+
+  constant CLK_PERIOD:  time := 10 ns;
+
+  procedure ClkCycle(
+    signal CLK: out std_logic;
+    CLK_PERIOD: time
+  ) is
+  begin
+    CLK <= '0';
+    wait for CLK_PERIOD/4;
+    CLK <= '1';
+    wait for CLK_PERIOD/2;
+    CLK <= '0';
+    wait for CLK_PERIOD/4;
+  end ClkCycle;
+
+  procedure ClkCycles(
+    N:  integer;
+    signal CLK: out std_logic;
+    CLK_PERIOD: time
+  ) is
+  begin
+    for i in 1 to N loop
+      ClkCycle(CLK, CLK_PERIOD);
+    end loop;
+  end ClkCycles;
+begin
+  JTAG_BLOCK: c4m_jtag_tap_controller
+    -- Use default values
+    port map (
+      TCK => TCK,
+      TMS => TMS,
+      TDI => TDI,
+      TDO => TDO,
+      TRST_N => TRST_N,
+      STATE => open,
+      IR => open,
+      CORE_OUT => "0",
+      CORE_IN => open,
+      CORE_EN => "0",
+      PAD_OUT => open,
+      PAD_IN => "0",
+      PAD_EN => open
+    );
+
+  SIM: process
+  begin
+    -- Reset
+    TCK <= '0';
+    TMS <= '1';
+    TDI <= '0';
+    TRST_N <= '0';
+    wait for 10*CLK_PERIOD;
+
+    TRST_N <= '1';
+    wait for CLK_PERIOD;
+
+    -- Enter RunTestIdle
+    TMS <= '0';
+    ClkCycle(TCK, CLK_PERIOD);
+    -- Enter SelectDRScan
+    TMS <= '1';
+    ClkCycle(TCK, CLK_PERIOD);
+    -- Enter Capture
+    TMS <= '0';
+    ClkCycle(TCK, CLK_PERIOD);
+    -- Enter Shift, run for 35 CLK cycles
+    TMS <= '0';
+    ClkCycles(35, TCK, CLK_PERIOD);
+    -- Enter Exit1
+    TMS <= '1';
+    ClkCycle(TCK, CLK_PERIOD);
+    -- Enter Update
+    TMS <= '1';
+    ClkCycle(TCK, CLK_PERIOD);
+    -- To TestLogicReset
+    TMS <= '1';
+    ClkCycles(4, TCK, CLK_PERIOD);
+
+    -- end simulation
+    wait;
+  end process;
+end rtl;
diff --git a/bench/vhdl/sampleshift.vhdl b/bench/vhdl/sampleshift.vhdl
new file mode 100644 (file)
index 0000000..62ec28b
--- /dev/null
@@ -0,0 +1,50 @@
+-- Test JTAG in the following way:
+--  * reset JTAG interface
+--  * load samplepreload command
+--  * shift in/out sampled inputs + wanted outputs
+--  * load extest command
+--  * execute
+
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity bench_sampleshift is
+end bench_sampleshift;
+
+architecture rtl of bench_sampleshift is
+  signal TCK:   std_logic;
+  signal TMS:   std_logic;
+  signal TDI:   std_logic;
+  signal TDO:   std_logic;
+  signal TRST_N: std_logic;
+  
+  constant CLK_PERIOD:  time := 10 ns;
+
+  procedure ClkCycle(
+    signal CLK: out std_logic;
+    CLK_PERIOD: time
+  ) is
+  begin
+    CLK <= '0';
+    wait for CLK_PERIOD/4;
+    CLK <= '1';
+    wait for CLK_PERIOD/2;
+    CLK <= '0';
+    wait for CLK_PERIOD/4;
+  end ClkCycle;
+
+  procedure ClkCycles(
+    N:  integer;
+    signal CLK: out std_logic;
+    CLK_PERIOD: time
+  ) is
+  begin
+    for i in 1 to N loop
+      ClkCycle(CLK, CLK_PERIOD);
+    end loop;
+  end ClkCycles;
+  
+  procedure LoadIR(
diff --git a/rtl/vhdl/c4m_jtag_idblock.vhdl b/rtl/vhdl/c4m_jtag_idblock.vhdl
new file mode 100644 (file)
index 0000000..d4353c1
--- /dev/null
@@ -0,0 +1,69 @@
+-- The JTAG id and bypass handling block
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity c4m_jtag_idblock is
+  generic (
+    IR_WIDTH:           integer := 2;
+    
+    PART_NUMBER:        std_logic_vector(15 downto 0);
+    VERSION:            std_logic_vector(3 downto 0) := "0100";
+    MANUFACTURER:       std_logic_vector(10 downto 0)
+  );
+  port (
+    -- needed TAP signals
+    TCK:        in std_logic;
+    TDI:        in std_logic;
+    TDO:        out std_logic;
+
+    -- JTAG state
+    STATE:      in TAPSTATE_TYPE;
+    NEXT_STATE: in TAPSTATE_TYPE;
+    DRSTATE:    in std_logic;
+
+    -- The instruction
+    IR:         in std_logic_vector(IR_WIDTH-1 downto 0)
+  );
+end c4m_jtag_idblock;
+
+architecture rtl of c4m_jtag_idblock is
+  constant IDCODE:      std_logic_vector(31 downto 0) := VERSION & PART_NUMBER & MANUFACTURER & "1";
+
+  signal SR_ID:         std_logic_vector(31 downto 0);
+  signal EN_TDO:        boolean;
+  
+  constant CMD_IDCODE:  std_logic_vector(IR_WIDTH-1 downto 0) := c4m_jtag_cmd_idcode(IR_WIDTH);
+  constant CMD_BYPASS:  std_logic_vector(IR_WIDTH-1 downto 0) := c4m_jtag_cmd_bypass(IR_WIDTH);
+begin
+  process (TCK)
+  begin
+    if rising_edge(TCK) then
+      if DRSTATE = '1' then
+        case STATE is
+          when Capture =>
+            SR_ID <= IDCODE;
+
+          when Shift =>
+            if IR = CMD_IDCODE then
+              SR_ID(30 downto 0) <= SR_ID(31 downto 1);
+              SR_ID(31) <= TDI;
+            elsif IR = CMD_BYPASS then
+              SR_ID(0) <= TDI;
+            else
+              null;
+            end if;
+
+          when others =>
+            null;
+        end case;
+      end if;
+    end if;
+  end process;
+
+  EN_TDO <= STATE = Shift and DRSTATE = '1' and (IR = CMD_IDCODE or IR = CMD_BYPASS);
+  TDO <= SR_ID(0) when EN_TDO else
+         'Z';
+end rtl;
diff --git a/rtl/vhdl/c4m_jtag_ioblock.vhdl b/rtl/vhdl/c4m_jtag_ioblock.vhdl
new file mode 100644 (file)
index 0000000..ec63440
--- /dev/null
@@ -0,0 +1,88 @@
+-- The block of io cells with JTAG boundary scan support
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity c4m_jtag_ioblock is
+  generic (
+    IR_WIDTH:   integer := 2;
+    IOS:        integer := 1
+  );
+  port (
+    -- needed TAP signals
+    TCK:        in std_logic;
+    TDI:        in std_logic;
+    TDO:        out std_logic;
+
+    -- JTAG state
+    STATE:      in TAPSTATE_TYPE;
+    NEXT_STATE: in TAPSTATE_TYPE;
+    DRSTATE:    in std_logic;
+
+    -- The instruction
+    IR:         in std_logic_vector(IR_WIDTH-1 downto 0);
+
+    -- The I/O access ports
+    CORE_OUT:   in std_logic_vector(IOS-1 downto 0);
+    CORE_IN:    out std_logic_vector(IOS-1 downto 0);
+    CORE_EN:    in std_logic_vector(IOS-1 downto 0);
+
+    -- The pad connections
+    PAD_OUT:    out std_logic_vector(IOS-1 downto 0);
+    PAD_IN:     in std_logic_vector(IOS-1 downto 0);
+    PAD_EN:     out std_logic_vector(IOS-1 downto 0)
+  );
+end c4m_jtag_ioblock;
+
+architecture rtl of c4m_jtag_ioblock is
+  signal IOMODE:        SRIOMODE_TYPE;
+  signal SAMPLEMODE:    SRSAMPLEMODE_TYPE;
+  signal ISSAMPLECMD:   boolean;
+  
+  signal BDSR_IN:       std_logic_vector(IOS-1 downto 0);
+  signal BDSR_OUT:      std_logic_vector(IOS-1 downto 0);
+  
+  constant CMD_SAMPLEPRELOAD: std_logic_vector(IR_WIDTH-1 downto 0) := c4m_jtag_cmd_samplepreload(IR_WIDTH);
+  constant CMD_EXTEST:  std_logic_vector(IR_WIDTH-1 downto 0) := c4m_jtag_cmd_extest(IR_WIDTH);
+begin
+  -- JTAG baundary scan IO cells
+  IOGEN: for i in 0 to IOS-1 generate
+  begin
+    IOCELL: c4m_jtag_iocell
+      port map (
+        CORE_IN => CORE_IN(i),
+        CORE_OUT => CORE_OUT(i),
+        CORE_EN => CORE_EN(i),
+        PAD_IN => PAD_IN(i),
+        PAD_OUT => PAD_OUT(i),
+        PAD_EN => PAD_EN(i),
+        BDSR_IN => BDSR_IN(i),
+        BDSR_OUT => BDSR_OUT(i),
+        IOMODE => IOMODE,
+        SAMPLEMODE => SAMPLEMODE,
+        TCK => TCK
+      );
+  end generate;
+  BDSRCONN: for i in 0 to IOS-2 generate
+  begin
+    BDSR_IN(i) <= BDSR_OUT(i+1);
+  end generate;
+  BDSR_IN(IOS-1) <= TDI;
+
+  -- Set IOMODE
+  -- Currently SR_2Core or SR_Z are not used
+  IOMODE <= SR_2Pad when IR = CMD_EXTEST else
+            SR_Through;
+
+  -- Set SAMPLEMODE
+  ISSAMPLECMD <= (IR = CMD_SAMPLEPRELOAD or IR = CMD_EXTEST) and DRSTATE = '1';
+  SAMPLEMODE <= SR_Sample when ISSAMPLECMD and STATE = Capture else
+                SR_Update when ISSAMPLECMD and STATE = Update else
+                SR_Shift when ISSAMPLECMD and STATE = Shift else
+                SR_Normal;
+
+  TDO <= BDSR_OUT(0) when ISSAMPLECMD and STATE = Shift else
+         'Z';
+end rtl;
diff --git a/rtl/vhdl/c4m_jtag_iocell.vhdl b/rtl/vhdl/c4m_jtag_iocell.vhdl
new file mode 100644 (file)
index 0000000..6a218a6
--- /dev/null
@@ -0,0 +1,101 @@
+-- An JTAG boundary scan for bidirectional I/O
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity c4m_jtag_iocell is
+  generic (
+    IR_WIDTH:   integer := 2
+  );
+  port (
+    -- core connections
+    CORE_IN:    out std_logic;
+    CORE_OUT:   in std_logic;
+    CORE_EN:    in std_logic;
+
+    -- pad connections
+    PAD_IN:     in std_logic;
+    PAD_OUT:    out std_logic;
+    PAD_EN:     out std_logic;
+
+    -- BD shift register
+    BDSR_IN:    in std_logic;
+    BDSR_OUT:   out std_logic;
+
+      -- Mode of I/O cell
+    IOMODE:     in SRIOMODE_TYPE;
+    SAMPLEMODE: in SRSAMPLEMODE_TYPE;
+    TCK:        in std_logic
+  );
+end c4m_jtag_iocell;
+
+architecture rtl of c4m_jtag_iocell is
+  signal SR_IOIN: std_logic;
+  signal SR_IOOUT: std_logic;
+  signal SR_IOEN: std_logic;
+
+  signal CORE_IN_BD: std_logic;
+  signal PAD_OUT_BD: std_logic;
+  signal PAD_EN_BD: std_logic;
+begin
+  with IOMODE select
+  CORE_IN <=
+    PAD_IN when SR_Through | SR_Z,
+    PAD_IN when SR_2Pad,
+    CORE_IN_BD when SR_2Core,
+    'X' when others;
+
+  with IOMODE select
+  PAD_OUT <=
+    CORE_OUT when SR_Through,
+    PAD_OUT_BD when SR_2Pad,
+    '0' when SR_2Core | SR_Z,  
+    'X' when others;
+
+  with IOMODE select
+  PAD_EN <=
+    CORE_EN when SR_Through,
+    PAD_EN_BD when SR_2Pad,
+    '0' when SR_2Core | SR_Z,
+    'X' when others;
+
+  process (TCK)
+  begin
+    -- Sampling of inputs and shifting of boundary scan SR needs to be done on
+    -- rising edge of TCK
+    if rising_edge(TCK) then
+      case SAMPLEMODE is
+        when SR_Sample =>
+          SR_IOIN <= PAD_IN;
+          SR_IOOUT <= CORE_OUT;
+          SR_IOEN <= CORE_EN;
+
+        when SR_Shift =>
+          SR_IOIN <= BDSR_IN;
+          SR_IOOUT <= SR_IOIN;
+          SR_IOEN <= SR_IOOUT;
+
+        when others =>
+          null;
+      end case;
+    end if;
+    
+    -- Update of output from boundary scan SR needs to be done on falling edge
+    -- of TCK
+    if falling_edge(TCK) then
+      case SAMPLEMODE is
+        when SR_Update =>
+          CORE_IN_BD <= SR_IOIN;
+          PAD_OUT_BD <= SR_IOOUT;
+          PAD_EN_BD <= SR_IOEN;
+          
+        when others =>
+          null;
+      end case;
+    end if;
+  end process;
+
+  BDSR_OUT <= SR_IOEN;
+end rtl;
diff --git a/rtl/vhdl/c4m_jtag_irblock.vhdl b/rtl/vhdl/c4m_jtag_irblock.vhdl
new file mode 100644 (file)
index 0000000..e8b34a5
--- /dev/null
@@ -0,0 +1,61 @@
+-- Handle the instruction register for the JTAG controller
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity c4m_jtag_irblock is
+  generic (
+    IR_WIDTH:   integer := 2
+  );
+  port (
+    -- needed TAP signals
+    TCK:        in std_logic;
+    TDI:        in std_logic;
+    TDO:        out std_logic;
+    
+    -- JTAG state
+    STATE:      in TAPSTATE_TYPE;
+    NEXT_STATE: in TAPSTATE_TYPE;
+    IRSTATE:    in std_logic;
+
+    -- instruction register
+    IR:         out std_logic_vector(IR_WIDTH-1 downto 0)
+  );
+end c4m_jtag_irblock;
+
+architecture rtl of c4m_jtag_irblock is
+  signal SHIFT_IR:      std_logic_vector(IR_WIDTH-1 downto 0);
+
+  constant CMD_IDCODE:  std_logic_vector(IR_WIDTH-1 downto 0) := c4m_jtag_cmd_idcode(IR_WIDTH);
+begin
+  process (TCK, STATE)
+  begin
+    if STATE = TestLogicReset then
+        SHIFT_IR <= (others => '0');
+        IR <= CMD_IDCODE;
+    elsif rising_edge(TCK) then
+      if IRSTATE = '1' then
+        case STATE is
+          when Capture =>
+            SHIFT_IR(1) <= '0';
+            SHIFT_IR(0) <= '1';
+
+          when Shift =>
+            SHIFT_IR(IR_WIDTH-2 downto 0) <= SHIFT_IR(IR_WIDTH-1 downto 1);
+            SHIFT_IR(IR_WIDTH-1) <= TDI;
+
+          when Update =>
+            IR <= SHIFT_IR;
+
+          when others =>
+            null;
+        end case;
+      end if;
+    end if;
+  end process;
+
+  TDO <= SHIFT_IR(0) when STATE = Shift and IRSTATE = '1' else
+         'Z';
+end rtl;
diff --git a/rtl/vhdl/c4m_jtag_pkg.vhdl b/rtl/vhdl/c4m_jtag_pkg.vhdl
new file mode 100644 (file)
index 0000000..87771f2
--- /dev/null
@@ -0,0 +1,213 @@
+-- Package of jtag support code from the Chips4Makers project
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+package c4m_jtag is
+  type TAPSTATE_TYPE is (
+    TestLogicReset,
+    RunTestIdle,
+    SelectDRScan,
+    SelectIRScan,
+    Capture,
+    Shift,
+    Exit1,
+    Pause,
+    Exit2,
+    Update
+  );
+  type SRIOMODE_TYPE is (
+    SR_Through, -- Connect core signal to pad signals
+    SR_2Pad,    -- Connect BD to pad
+    SR_2Core,   -- Connect BD to core
+    SR_Z        -- pad is high impedance
+  );
+  type SRSAMPLEMODE_TYPE is (
+    SR_Normal,  -- No sampling or shifting
+    SR_Sample,  -- Sample IO state in BD SR on rising edge of TCK
+    SR_Update,  -- Update BD from SR on falling edge of TCK
+    SR_Shift    -- Shift the BD SR
+  );
+
+  component c4m_jtag_tap_fsm is
+    port (
+      -- The TAP signals
+      TCK:      in std_logic;
+      TMS:      in std_logic;
+      TRST_N:   in std_logic;
+
+      -- The state outputs
+      STATE:    out TAPSTATE_TYPE;
+      NEXT_STATE: out TAPSTATE_TYPE;
+      DRSTATE:  out std_logic;
+      IRSTATE:  out std_logic
+    );
+  end component c4m_jtag_tap_fsm;
+
+  component c4m_jtag_irblock is
+    generic (
+      IR_WIDTH: integer := 2
+    );
+    port (
+      -- needed TAP signals
+      TCK:      in std_logic;
+      TDI:      in std_logic;
+      TDO:      out std_logic;
+    
+      -- JTAG state
+      STATE:    in TAPSTATE_TYPE;
+      NEXT_STATE: in TAPSTATE_TYPE;
+      IRSTATE:  in std_logic;
+
+      -- instruction register
+      IR:       out std_logic_vector(IR_WIDTH-1 downto 0)
+    );
+  end component c4m_jtag_irblock;
+  
+  component c4m_jtag_idblock is
+    generic (
+      IR_WIDTH:         integer := 2;
+    
+      PART_NUMBER:      std_logic_vector(15 downto 0);
+      VERSION:          std_logic_vector(3 downto 0) := "0000";
+      MANUFACTURER:     std_logic_vector(10 downto 0)
+    );
+    port (
+      -- needed TAP signals
+      TCK:      in std_logic;
+      TDI:      in std_logic;
+      TDO:      out std_logic;
+
+      -- JTAG state
+      STATE:    in TAPSTATE_TYPE;
+      NEXT_STATE: in TAPSTATE_TYPE;
+      DRSTATE:  in std_logic;
+
+      -- The instruction
+      IR:       in std_logic_vector(IR_WIDTH-1 downto 0)
+    );
+  end component c4m_jtag_idblock;
+
+  component c4m_jtag_iocell is
+    port (
+      -- core connections
+      CORE_IN:  out std_logic;
+      CORE_OUT: in std_logic;
+      CORE_EN:  in std_logic;
+
+      -- pad connections
+      PAD_IN:   in std_logic;
+      PAD_OUT:  out std_logic;
+      PAD_EN:   out std_logic;
+
+      -- BD shift register
+      BDSR_IN:  in std_logic;
+      BDSR_OUT: out std_logic;
+
+      -- Mode of I/O cell
+      IOMODE:   in SRIOMODE_TYPE;
+      SAMPLEMODE: in SRSAMPLEMODE_TYPE;
+      TCK:      in std_logic
+    );
+  end component c4m_jtag_iocell;
+  
+  component c4m_jtag_ioblock is
+    generic (
+      IR_WIDTH: integer := 2;
+      IOS:      integer := 1
+    );
+    port (
+      -- needed TAP signals
+      TCK:      in std_logic;
+      TDI:      in std_logic;
+      TDO:      out std_logic;
+
+      -- JTAG state
+      STATE:    in TAPSTATE_TYPE;
+      NEXT_STATE: in TAPSTATE_TYPE;
+      DRSTATE:  in std_logic;
+
+      -- The instruction
+      IR:       in std_logic_vector(IR_WIDTH-1 downto 0);
+
+      -- The I/O access ports
+      CORE_OUT: in std_logic_vector(IOS-1 downto 0);
+      CORE_IN:  out std_logic_vector(IOS-1 downto 0);
+      CORE_EN:  in std_logic_vector(IOS-1 downto 0);
+
+      -- The pad connections
+      PAD_OUT:  out std_logic_vector(IOS-1 downto 0);
+      PAD_IN:   in std_logic_vector(IOS-1 downto 0);
+      PAD_EN:   out std_logic_vector(IOS-1 downto 0)
+    );
+  end component c4m_jtag_ioblock;
+
+  component c4m_jtag_tap_controller is
+    generic (
+      IR_WIDTH: integer := 2;
+      IOS:      integer := 1;
+
+      VERSION:  std_logic_vector(3 downto 0) := "0000"
+    );
+    port (
+      -- The TAP signals
+      TCK:      in std_logic;
+      TMS:      in std_logic;
+      TDI:      in std_logic;
+      TDO:      out std_logic;
+      TRST_N:   in std_logic;
+
+      -- The FSM state indicators
+      STATE:    out TAPSTATE_TYPE;
+      NEXT_STATE: out TAPSTATE_TYPE;
+      DRSTATE:  out std_logic;
+
+      -- The Instruction Register
+      IR:       out std_logic_vector(IR_WIDTH-1 downto 0);
+
+      -- The I/O access ports
+      CORE_IN:  out std_logic_vector(IOS-1 downto 0);
+      CORE_EN:  in std_logic_vector(IOS-1 downto 0);
+      CORE_OUT: in std_logic_vector(IOS-1 downto 0);
+
+      -- The pad connections
+      PAD_IN:   in std_logic_vector(IOS-1 downto 0);
+      PAD_EN:   out std_logic_vector(IOS-1 downto 0);
+      PAD_OUT:  out std_logic_vector(IOS-1 downto 0)
+    );
+  end component c4m_jtag_tap_controller;
+
+  function c4m_jtag_cmd_idcode(width: integer) return std_logic_vector;
+  function c4m_jtag_cmd_bypass(width: integer) return std_logic_vector;
+  function c4m_jtag_cmd_samplepreload(width: integer) return std_logic_vector;
+  function c4m_jtag_cmd_extest(width: integer) return std_logic_vector;
+end c4m_jtag;
+
+package body c4m_jtag is
+  function c4m_jtag_cmd_bypass(width: integer) return std_logic_vector is
+    variable return_vector: std_logic_vector(width-1 downto 0);
+  begin
+    return_vector := (others => '1');
+    return return_vector;
+  end;
+
+  function c4m_jtag_cmd_idcode(width: integer) return std_logic_vector is
+    variable return_vector: std_logic_vector(width-1 downto 0);
+  begin
+    return_vector := (0 => '1', others => '0');
+    return return_vector;
+  end;
+
+  function c4m_jtag_cmd_samplepreload(width: integer) return std_logic_vector is
+    variable return_vector: std_logic_vector(width-1 downto 0);
+  begin
+    return_vector := (1 => '1', others => '0');
+    return return_vector;
+  end;
+
+  function c4m_jtag_cmd_extest(width: integer) return std_logic_vector is
+    variable return_vector: std_logic_vector(width-1 downto 0);
+  begin
+    return_vector := (others => '0');
+    return return_vector;
+  end;
+end package body;
diff --git a/rtl/vhdl/c4m_jtag_tap_controller.vhdl b/rtl/vhdl/c4m_jtag_tap_controller.vhdl
new file mode 100644 (file)
index 0000000..a6f0f15
--- /dev/null
@@ -0,0 +1,128 @@
+-- A JTAG complient tap controller implementation
+-- This is implemented based on the IEEE 1149.1 standard
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity c4m_jtag_tap_controller is
+  generic (
+    IR_WIDTH:   integer := 2;
+    IOS:        integer := 1;
+
+    VERSION:    std_logic_vector(3 downto 0)
+  );
+  port (
+    -- The TAP signals
+    TCK:        in std_logic;
+    TMS:        in std_logic;
+    TDI:        in std_logic;
+    TDO:        out std_logic;
+    TRST_N:     in std_logic;
+
+    -- The FSM state indicators
+    STATE:      out TAPSTATE_TYPE;
+    NEXT_STATE: out TAPSTATE_TYPE;
+    DRSTATE:    out std_logic;
+
+    -- The Instruction Register
+    IR:         out std_logic_vector(IR_WIDTH-1 downto 0);
+
+    -- The I/O access ports
+    CORE_IN:    out std_logic_vector(IOS-1 downto 0);
+    CORE_EN:    in std_logic_vector(IOS-1 downto 0);
+    CORE_OUT:   in std_logic_vector(IOS-1 downto 0);
+
+    -- The pad connections
+    PAD_IN:     in std_logic_vector(IOS-1 downto 0);
+    PAD_EN:     out std_logic_vector(IOS-1 downto 0);
+    PAD_OUT:    out std_logic_vector(IOS-1 downto 0)
+  );
+end c4m_jtag_tap_controller;
+
+architecture rtl of c4m_jtag_tap_controller is
+  signal S_STATE:       TAPSTATE_TYPE;
+  signal S_NEXT_STATE:  TAPSTATE_TYPE;
+  signal S_IRSTATE:       std_logic;
+  signal S_DRSTATE:       std_logic;
+  signal S_IR:          std_logic_vector(IR_WIDTH-1 downto 0);
+
+  -- TODO: Automate PART_NUMBER generation
+  constant PART_NUMBER: std_logic_vector(15 downto 0) := "0000000010001001";
+  -- TODO: Get manufacturer ID
+  constant MANUFACTURER: std_logic_vector(10 downto 0) := "00000000000";
+begin
+  STATE <= S_STATE;
+  NEXT_STATE <= S_NEXT_STATE;
+  DRSTATE <= S_DRSTATE;
+  IR <= S_IR;
+
+  -- JTAG state machine
+  FSM:  c4m_jtag_tap_fsm
+    port map (
+      TCK => TCK,
+      TMS => TMS,
+      TRST_N => TRST_N,
+      STATE => S_STATE,
+      NEXT_STATE => S_NEXT_STATE,
+      DRSTATE => S_DRSTATE,
+      IRSTATE => S_IRSTATE
+    );
+
+  -- The instruction register
+  IRBLOCK: c4m_jtag_irblock
+    generic map (
+      IR_WIDTH => IR_WIDTH
+    )
+    port map (
+      TCK => TCK,
+      TDI => TDI,
+      TDO => TDO,
+      STATE => S_STATE,
+      NEXT_STATE => S_NEXT_STATE,
+      IRSTATE => S_IRSTATE,
+      IR => S_IR
+    );
+
+  -- The ID
+  IDBLOCK: c4m_jtag_idblock
+    generic map (
+      IR_WIDTH => IR_WIDTH,
+      PART_NUMBER => PART_NUMBER,
+      MANUFACTURER => MANUFACTURER
+    )
+    port map (
+      TCK => TCK,
+      TDI => TDI,
+      TDO => TDO,
+      STATE => S_STATE,
+      NEXT_STATE => S_NEXT_STATE,
+      DRSTATE => S_DRSTATE,
+      IR => S_IR
+    );
+  
+  -- The IOS
+  IOBLOCK: c4m_jtag_ioblock
+    generic map (
+      IR_WIDTH => IR_WIDTH,
+      IOS => IOS
+    )
+    port map (
+      TCK => TCK,
+      TDI => TDI,
+      TDO => TDO,
+      STATE => S_STATE,
+      NEXT_STATE => S_NEXT_STATE,
+      DRSTATE => S_DRSTATE,
+      IR => S_IR,
+      CORE_OUT => CORE_OUT,
+      CORE_IN => CORE_IN,
+      CORE_EN => CORE_EN,
+      PAD_OUT => PAD_OUT,
+      PAD_IN => PAD_IN,
+      PAD_EN => PAD_EN
+    );
+end rtl;
+
+
diff --git a/rtl/vhdl/c4m_jtag_tap_fsm.vhdl b/rtl/vhdl/c4m_jtag_tap_fsm.vhdl
new file mode 100644 (file)
index 0000000..005eccb
--- /dev/null
@@ -0,0 +1,140 @@
+-- The JTAG state machine
+-- This is implemented based on the IEEE 1149.1 standard
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity c4m_jtag_tap_fsm is
+  port (
+    -- The TAP signals
+    TCK:        in std_logic;
+    TMS:        in std_logic;
+    TRST_N:     in std_logic;
+
+    -- The state outputs
+    STATE:      out TAPSTATE_TYPE;
+    NEXT_STATE: out TAPSTATE_TYPE;
+    DRSTATE:    out std_logic;
+    IRSTATE:    out std_logic
+  );
+end c4m_jtag_tap_fsm;
+
+architecture rtl of c4m_jtag_tap_fsm is
+  signal S_STATE:         TAPSTATE_TYPE;
+  signal S_NEXT_STATE:    TAPSTATE_TYPE;
+  signal S_DRSTATE:       std_logic;
+  signal S_IRSTATE:       std_logic;
+  signal NEXT_DRSTATE:    std_logic;
+  signal NEXT_IRSTATE:    std_logic;
+begin
+  STATE <= S_STATE;
+  NEXT_STATE <= S_NEXT_STATE;
+  DRSTATE <= S_DRSTATE;
+  IRSTATE <= S_IRSTATE;
+  
+  process (TCK, TRST_N)
+  begin
+    if TRST_N = '0' then
+      S_DRSTATE <= '0';
+      S_IRSTATE <= '0';
+      S_STATE <= TestLogicReset;
+    elsif rising_edge(TCK) then
+      S_STATE <= S_NEXT_STATE;
+      S_DRSTATE <= NEXT_DRSTATE;
+      S_IRSTATE <= NEXT_IRSTATE;
+    end if;
+  end process;
+
+  NEXT_DRSTATE <=
+    '0' when S_NEXT_STATE = TestLogicReset else
+    '0' when S_NEXT_STATE = RunTestIdle else
+    '1' when S_NEXT_STATE = SelectDRScan else
+    '0' when S_NEXT_STATE = SelectIRScan else
+    S_DRSTATE;
+  NEXT_IRSTATE <=
+    '0' when S_NEXT_STATE = TestLogicReset else
+    '0' when S_NEXT_STATE = RunTestIdle else
+    '0' when S_NEXT_STATE = SelectDRScan else
+    '1' when S_NEXT_STATE = SelectIRScan else
+    S_IRSTATE;
+
+  process (S_STATE, TMS)
+  begin
+    case S_STATE is
+      when TestLogicReset =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= RunTestIdle;
+        else
+          S_NEXT_STATE <= TestLogicReset;
+        end if;
+
+      when RunTestIdle =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= RunTestIdle;
+        else
+          S_NEXT_STATE <= SelectDRScan;
+        end if;
+
+      when SelectDRScan =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Capture;
+        else
+          S_NEXT_STATE <= SelectIRScan;
+        end if;
+
+      when SelectIRScan =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Capture;
+        else
+          S_NEXT_STATE <= TestLogicReset;
+        end if;
+
+      when Capture =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Shift;
+        else
+          S_NEXT_STATE <= Exit1;
+        end if;
+
+      when Shift =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Shift;
+        else
+          S_NEXT_STATE <= Exit1;
+        end if;
+
+      when Exit1 =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Pause;
+        else
+          S_NEXT_STATE <= Update;
+        end if;
+
+      when Pause =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Pause;
+        else
+          S_NEXT_STATE <= Exit2;
+        end if;
+
+      when Exit2 =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= Shift;
+        else
+          S_NEXT_STATE <= Update;
+        end if;
+
+      when Update =>
+        if (TMS = '0') then
+          S_NEXT_STATE <= RunTestIdle;
+        else
+          S_NEXT_STATE <= SelectDRScan;
+        end if;
+
+      when others =>
+        S_NEXT_STATE <= TestLogicReset;
+    end case;
+  end process;
+end rtl;
diff --git a/sim/cocotb/c4m_jtag.py b/sim/cocotb/c4m_jtag.py
new file mode 100644 (file)
index 0000000..5deb89a
--- /dev/null
@@ -0,0 +1,201 @@
+import cocotb
+from cocotb.triggers import Timer
+from cocotb.utils import get_sim_steps
+from cocotb.binary import BinaryValue
+
+class JTAG_Clock(object):
+    """
+    Class for the JTAG clock, run cycle by cycle
+    """
+    def __init__(self, signal, period):
+        self.signal = signal
+        self.t = Timer(period/4)
+
+    @cocotb.coroutine
+    def Cycle(self, cycles=1):
+        """
+        Do one or more cycles
+        Cycle start in middle of 0 pulse of the clock
+        """
+        for i in range(cycles):
+            self.signal <= 0
+            yield self.t
+            self.signal <= 1
+            yield self.t
+            yield self.t
+            self.signal <= 0
+            yield self.t
+
+class JTAG_Master(object):
+    """
+    Class that will run JTAG commands, shift in and out data
+    """
+    #TODO: Handle a JTAG chain with more than one device
+
+    def __init__(self, tck, tms, tdi, tdo, trst_n=None, clk_period=1000):
+        self.tck = tck
+        self.clkgen = JTAG_Clock(tck, clk_period)
+        tck <= 0
+        self.tms = tms
+        tms <= 1
+        self.tdi = tdi
+        tdi <= 0
+        self.tdo = tdo
+        self.trst_n = trst_n
+        trst_n <= 1
+        self.period = Timer(clk_period)
+
+        # Standard commands
+        # TODO: make IR length configurable; now 2 is assumed
+        self.BYPASS = [1, 1]
+        self.IDCODE = [0, 1]
+        self.SAMPLEPRELOAD = [1, 0]
+        self.EXTEST = [0, 0]
+
+        # After command we always leave the controller in reset or runidle state
+        # If value is None we will always reset the interface
+        self.state = None
+
+        # The methods of this class are coroutines. The results will be stored
+        # in the result field
+        self.result = None
+
+    @cocotb.coroutine
+    def cycle_clock(self, cycles=1):
+        if self.state == "Run" and self.tms:
+            self.state = "Scan"
+        yield self.clkgen.Cycle(cycles)
+
+    @cocotb.coroutine
+    def reset(self):
+        if not self.trst_n is None:
+            # Enable reset signal for one clock period
+            self.trst_n <= 0
+            yield self.period
+            self.trst_n <= 1
+        else:
+            # 5 cycles with tms on 1 should reset the JTAG TAP controller
+            self.tms <= 1
+            yield self.cycle_clock(5)
+
+        self.state = "Reset"
+
+        self.result = None
+
+    @cocotb.coroutine
+    def change_state(self, tms_list):
+        tms_copy = list(tms_list)
+        while tms_copy:
+            self.tms <= tms_copy.pop()
+            yield self.cycle_clock()
+        self.result = None
+
+    @cocotb.coroutine
+    def change_to_run(self):
+        """
+        Put TAP in RunTestIdle state
+        self.result is bool and true if TAP went through reset state
+        """
+        isreset = False
+        if self.state is None:
+            yield self.reset()
+        if self.state is "Reset":
+            isreset = True
+            self.tms <= 0
+            yield self.cycle_clock()
+            self.state = "Run"
+        assert(self.state == "Run")
+        self.result = isreset
+
+    @cocotb.coroutine
+    def load_ir(self, cmd):
+        cmd_copy = list(cmd)
+        result = BinaryValue(bits=len(cmd_copy))
+        l_result = list()
+
+        yield self.change_to_run()
+        # Go to Capture/IR
+        yield self.change_state([0, 1, 1])
+
+        # Shift the two
+        self.tms <= 0
+        while cmd_copy:
+            # In first iteration we enter SHIFT state and tdo is made active
+            yield self.cycle_clock()
+            # For the last iteration tdi will be shifted when entering next state
+            self.tdi <= cmd_copy.pop()
+            l_result.insert(0, str(self.tdo))
+
+        # Go to RunTestIdle
+        yield self.change_state([0, 1, 1])
+        self.state = "Run"
+
+    @cocotb.coroutine
+    def idcode(self):
+        """
+        Get the IDCODE from the device
+        result will contain the 32 bit IDCODE of the device
+        """
+
+        result = BinaryValue(bits=32)
+        l_result = list()
+
+        # Keep tdi 0 for the whole run
+        self.tdi <= 0
+
+        yield self.change_to_run()
+        if not self.result:
+            # If TAP was not reset we have to load IDCODE command
+            yield self.load_ir(self.IDCODE)
+
+        # Should be again in RUN state
+        assert(self.state == "Run")
+
+        # Go to Shift/DR
+        yield self.change_state([0, 0, 1])
+
+        # Enter Shift; run for 32 cycles
+        self.tms <= 0
+        for i in range(32):
+            l_result.insert(0, str(self.tdo))
+            yield self.cycle_clock()
+        result.binstr = "".join(l_result)
+
+        # Go to RunTestIdle
+        yield self.change_state([0, 1, 1])
+        self.state = "Run"
+
+        self.result = result
+
+    @cocotb.coroutine
+    def shift_data(self, data_in):
+        """
+        Shift data in through the JTAG and capture the output
+        Input can be of type BinaryValue or an iterable value of 0 and 1s.
+        Last bit will be shifted in first.
+        result will contain the sample TDO with the same number of bits as the input
+        """
+        if isinstance(data_in, BinaryValue):
+            data_copy = [int(c) for c in data_in.binstr]
+        else:
+            data_copy = list(data_in)
+        result = BinaryValue()
+        l_result = list()
+
+        yield self.change_to_run()
+        # Go to Capture/DR
+        yield self.change_state([0, 1])
+
+        # Shift data through
+        self.tms <= 0
+        while data_copy:
+            yield self.cycle_clock()
+            self.tdi <= data_copy.pop()
+            l_result.insert(0, str(self.tdo))
+        result.binstr = "".join(l_result)
+
+        # Go to RunTestIdle
+        yield self.change_state([0, 1, 1])
+        self.state = "Run"
+
+        self.result = result
diff --git a/sim/cocotb/controller/Makefile b/sim/cocotb/controller/Makefile
new file mode 100644 (file)
index 0000000..336d7e5
--- /dev/null
@@ -0,0 +1,19 @@
+COCOTB=$(HOME)/eda/code/cocotb
+VHDLDIR=$(HOME)/eda/code/c4m_jtag/rtl/vhdl
+VHDL_SOURCES = \
+  $(VHDLDIR)/c4m_jtag_pkg.vhdl \
+  $(VHDLDIR)/c4m_jtag_tap_fsm.vhdl \
+  $(VHDLDIR)/c4m_jtag_irblock.vhdl \
+  $(VHDLDIR)/c4m_jtag_iocell.vhdl \
+  $(VHDLDIR)/c4m_jtag_ioblock.vhdl \
+  $(VHDLDIR)/c4m_jtag_idblock.vhdl \
+  $(VHDLDIR)/c4m_jtag_tap_controller.vhdl
+TOPLEVEL=c4m_jtag_tap_controller
+TOPLEVEL_LANG=vhdl
+MODULE=test
+SIM=ghdl
+GPI_IMPL=vhpi
+SIM_ARGS=--wave=test.ghw
+
+include $(COCOTB)/makefiles/Makefile.inc
+include $(COCOTB)/makefiles/Makefile.sim
diff --git a/sim/cocotb/controller/c4m_jtag.py b/sim/cocotb/controller/c4m_jtag.py
new file mode 120000 (symlink)
index 0000000..f409baa
--- /dev/null
@@ -0,0 +1 @@
+../c4m_jtag.py
\ No newline at end of file
diff --git a/sim/cocotb/controller/test.py b/sim/cocotb/controller/test.py
new file mode 100644 (file)
index 0000000..c29d00e
--- /dev/null
@@ -0,0 +1,95 @@
+import cocotb
+from cocotb.triggers import Timer
+from cocotb.utils import get_sim_steps
+from cocotb.binary import BinaryValue
+
+from c4m_jtag import JTAG_Master
+
+@cocotb.test()
+def test01_idcode(dut):
+    """
+    Test the IDCODE command
+    """
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master = JTAG_Master(dut.tck, dut.tms, dut.tdi, dut.tdo, dut.trst_n, clk_period)
+
+    dut._log.info("Trying to get IDCODE...")
+
+    yield master.idcode()
+    result1 = master.result
+    dut._log.info("IDCODE1: {}".format(result1))
+
+    yield master.idcode()
+    result2 = master.result
+    dut._log.info("IDCODE2: {}".format(result2))
+
+    assert(result1 == result2)
+    
+@cocotb.test()
+def test02_bypass(dut):
+    """
+    Test of BYPASS mode
+    """
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master = JTAG_Master(dut.tck, dut.tms, dut.tdi, dut.tdo, dut.trst_n, clk_period)
+
+    dut._log.info("Loading BYPASS command")
+    yield master.load_ir(master.BYPASS)
+
+    dut._log.info("Sending data")
+
+    data_in = BinaryValue()
+    data_in.binstr = "01001101"
+    yield master.shift_data(data_in)
+
+    dut._log.info("bypass out: {}".format(master.result.binstr))
+    assert(master.result.binstr[:-1] == data_in.binstr[1:])
+
+@cocotb.test()
+def test03_sample(dut):
+    """
+    Test of SAMPLEPRELOAD and EXTEST
+    """
+    data_in = BinaryValue()
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master = JTAG_Master(dut.tck, dut.tms, dut.tdi, dut.tdo, dut.trst_n, clk_period)
+
+
+    dut._log.info("Load SAMPLEPRELOAD command")
+    yield master.load_ir(master.SAMPLEPRELOAD)
+
+    data_in.binstr = "011"
+    dut._log.info("  preloading data {}".format(data_in.binstr))
+
+    # Set the ios pins
+    dut.core_out = 0
+    dut.core_en = 0
+    dut.pad_in = 1
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert(master.result.binstr == "100")
+
+
+    dut._log.info("Load EXTEST command")
+    yield master.load_ir(master.EXTEST)
+
+    data_in.binstr = "100"
+    dut._log.info("  input data {}".format(data_in.binstr))
+    
+    # Set the ios pins
+    dut.core_out = 1
+    dut.core_en = 1
+    dut.pad_in = 0
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert(master.result.binstr == "011")
+
+    dut._log.info("Do a capture of the last loaded data")
+    yield master.shift_data([])
+
diff --git a/sim/cocotb/dual_parallel/Makefile b/sim/cocotb/dual_parallel/Makefile
new file mode 100644 (file)
index 0000000..f692e4f
--- /dev/null
@@ -0,0 +1,20 @@
+COCOTB=$(HOME)/eda/code/cocotb
+VHDLDIR=$(HOME)/eda/code/c4m_jtag/rtl/vhdl
+VHDL_SOURCES = \
+  $(VHDLDIR)/c4m_jtag_pkg.vhdl \
+  $(VHDLDIR)/c4m_jtag_tap_fsm.vhdl \
+  $(VHDLDIR)/c4m_jtag_irblock.vhdl \
+  $(VHDLDIR)/c4m_jtag_iocell.vhdl \
+  $(VHDLDIR)/c4m_jtag_ioblock.vhdl \
+  $(VHDLDIR)/c4m_jtag_idblock.vhdl \
+  $(VHDLDIR)/c4m_jtag_tap_controller.vhdl \
+  $(PWD)/dual_parallel.vhdl
+TOPLEVEL=dual_parallel
+TOPLEVEL_LANG=vhdl
+MODULE=test
+SIM=ghdl
+GPI_IMPL=vhpi
+SIM_ARGS=--wave=test.ghw
+
+include $(COCOTB)/makefiles/Makefile.inc
+include $(COCOTB)/makefiles/Makefile.sim
diff --git a/sim/cocotb/dual_parallel/c4m_jtag.py b/sim/cocotb/dual_parallel/c4m_jtag.py
new file mode 120000 (symlink)
index 0000000..f409baa
--- /dev/null
@@ -0,0 +1 @@
+../c4m_jtag.py
\ No newline at end of file
diff --git a/sim/cocotb/dual_parallel/dual_parallel.vhdl b/sim/cocotb/dual_parallel/dual_parallel.vhdl
new file mode 100644 (file)
index 0000000..0e99559
--- /dev/null
@@ -0,0 +1,78 @@
+-- Top cell with two instantiations of the tap_controller with parallel scan chains
+
+library ieee;
+use ieee.std_logic_1164.ALL;
+
+use work.c4m_jtag.ALL;
+
+entity dual_parallel is
+  port (
+    -- Instance 1
+    -- ==========
+    -- JTAG
+    I1_TCK:     in std_logic;
+    I1_TMS:     in std_logic;
+    I1_TDI:     in std_logic;
+    I1_TDO:     out std_logic;
+    I1_TRST_N:  in std_logic;
+
+    -- Instance 2
+    -- ==========
+    -- JTAG
+    I2_TCK:     in std_logic;
+    I2_TMS:     in std_logic;
+    I2_TDI:     in std_logic;
+    I2_TDO:     out std_logic;
+    I2_TRST_N:  in std_logic
+  );
+end dual_parallel;
+
+architecture rtl of dual_parallel is
+  signal I1_PAD_IN:     std_logic;
+  signal I1_PAD_EN:     std_logic;
+  signal I1_PAD_OUT:    std_logic;
+  signal I2_PAD_IN:     std_logic;
+  signal I2_PAD_EN:     std_logic;
+  signal I2_PAD_OUT:    std_logic;
+begin
+  CTRL1: c4m_jtag_tap_controller
+    port map (
+      TCK => I1_TCK,
+      TMS => I1_TMS,
+      TDI => I1_TDI,
+      TDO => I1_TDO,
+      TRST_N => I1_TRST_N,
+      STATE => open,
+      NEXT_STATE => open,
+      IR => open,
+      CORE_IN => open,
+      CORE_EN => "1",
+      CORE_OUT => "1",
+      PAD_IN(0) => I1_PAD_IN,
+      PAD_EN(0) => I1_PAD_EN,
+      PAD_OUT(0) => I1_PAD_OUT
+    );
+
+  CTRL2: c4m_jtag_tap_controller
+    port map (
+      TCK => I2_TCK,
+      TMS => I2_TMS,
+      TDI => I2_TDI,
+      TDO => I2_TDO,
+      TRST_N => I2_TRST_N,
+      STATE => open,
+      NEXT_STATE => open,
+      IR => open,
+      CORE_IN => open,
+      CORE_EN => "1",
+      CORE_OUT => "0",
+      PAD_IN(0) => I2_PAD_IN,
+      PAD_EN(0) => I2_PAD_EN,
+      PAD_OUT(0) => I2_PAD_OUT
+    );
+
+  I1_PAD_IN <= I2_PAD_OUT when I2_PAD_EN = '1' else
+               'Z';
+  I2_PAD_IN <= I1_PAD_OUT when I1_PAD_EN = '1' else
+               'Z';
+end rtl;
diff --git a/sim/cocotb/dual_parallel/test.py b/sim/cocotb/dual_parallel/test.py
new file mode 100644 (file)
index 0000000..51f868d
--- /dev/null
@@ -0,0 +1,43 @@
+import cocotb
+from cocotb.triggers import Timer
+from cocotb.utils import get_sim_steps
+from cocotb.binary import BinaryValue
+
+from c4m_jtag import JTAG_Master
+
+@cocotb.test()
+def test01_dual(dut):
+    """
+    Test the IDCODE command
+    """
+
+    # TODO: Allow parallel operation of the JTAG chains
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master1 = JTAG_Master(dut.i1_tck, dut.i1_tms, dut.i1_tdi, dut.i1_tdo, dut.i1_trst_n, clk_period)
+    master2 = JTAG_Master(dut.i2_tck, dut.i2_tms, dut.i2_tdi, dut.i2_tdo, dut.i2_trst_n, clk_period)
+
+    dut._log.info("Set command to SAMPLEPRELOAD")
+    yield master1.load_ir(master1.SAMPLEPRELOAD)
+    yield master2.load_ir(master2.SAMPLEPRELOAD)
+    
+    dut._log.info("Load data, scan out first sample")
+    yield master1.shift_data([0, 0, 0])
+    dut._log.info("  master1 scan_out: {}".format(master1.result.binstr))
+    assert(master1.result.binstr == "011")
+    yield master2.shift_data([1, 1, 1])
+    dut._log.info("  master2 scan_out: {}".format(master2.result.binstr))
+    assert(master2.result.binstr == "101")
+    
+    dut._log.info("Set command to EXTEST")
+    yield master1.load_ir(master1.EXTEST)
+    yield master2.load_ir(master2.EXTEST)
+
+    dut._log.info("Second scan")
+    yield master1.shift_data([0, 0, 0])
+    dut._log.info("  master1 scan_out: {}".format(master1.result.binstr))
+    assert(master1.result.binstr == "111")
+    yield master2.shift_data([1, 1, 1])
+    dut._log.info("  master2 scan_out: {}".format(master2.result.binstr))
+    assert(master2.result.binstr == "Z01")
diff --git a/sim/cocotb/dual_serial/TODO b/sim/cocotb/dual_serial/TODO
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/sim/ghdl/bench_idcode.sh b/sim/ghdl/bench_idcode.sh
new file mode 100755 (executable)
index 0000000..1717ae0
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+ghdl -i ../../rtl/vhdl/c4m_jtag_*.vhdl
+ghdl -i ../../bench/vhdl/idcode.vhdl
+ghdl -m bench_idcode
+./bench_idcode --wave=bench_idcode.ghw