[J-core] Instructions and Databus

Geoff Salmon gsalmon at se-instruments.com
Tue Jun 21 12:55:01 EDT 2016

Hi Santiago

On 16-06-21 11:48 AM, Gutierrez, Santiago wrote:
> I was wondering if I could get an explanation of the two instr_bus types
> as well as the two data_bus types. I am making an interface between the
> J2 core and a memory controller and knowing the details of what gets put
> out and taken in by the CPU would be very helpful. I saw that they just
> seem to be arrays of the cpu_instruction and cpu_data types
> respectively. Some clarification on those types and what some of their
> contents are would be welcome.

> Specifically what are the jp and en signals in the cpu_instruction_o_t?
> I believe the ‘a’ signal is just the address of the next instruction, is
> this correct?

Yes. As you've seen, the ports of the cpu used for fetching instructions are

inst_o : out cpu_instruction_o_t;
inst_i : in cpu_instruction_i_t;

and those types are in soc_top/components/cpu/cpu2j0_pkg.vhd

type cpu_instruction_o_t is record
en : std_logic; -- enable
a : std_logic_vector(31 downto 1); -- address
jp : std_logic; -- jump
end record;

type cpu_instruction_i_t is record
d : std_logic_vector(15 downto 0);
ack : std_logic;
end record;

To fetch an instruction, the cpu will set inst_o.en <= '1' and inst_o.a 
<= the address of the instruction. Note a's type is 31 downto 1 instead 
of 31 downto 0 because instructions are 16-bit aligned, so lowest bit in 
the address is always 0.

After inst_o.en is raised, the cpu will latch inst_i.ack and inst_i.d at 
rising clock edges. If inst_i.ack='1', then the instruction fetch is 
complete and inst_i.d will be fed to the cpu's instruction decoder. If 
inst_i.ack='0', the cpu will continue waiting for ack to go high and the 
cpu will stall.

inst_o.jp is meant to indicate when a jump has occurred. It should go 
high when the address being fetched did not come from the usual PC+2 
logic. You can probably ignore that initially.

> Also what are the en and we signals in the cpu_data_o_t?

These are cpu ports

       db_o         : out cpu_data_o_t;
       db_i         : in  cpu_data_i_t;

and types

    type cpu_data_o_t is record
       en   : std_logic;
       a    : std_logic_vector(31 downto 0);
       rd   : std_logic;
       wr   : std_logic;
       we   : std_logic_vector(3 downto 0);
       d    : std_logic_vector(31 downto 0);
    end record;

    type cpu_data_i_t is record
       d    : std_logic_vector(31 downto 0);
       ack  : std_logic;
    end record;

Reading from data memory operates the same as fetching an instruction. 
The cpu sets db_o.en <= '1' and db_i.a <= the address to read. Reads are 
always 32 bits and internally the align_read_data function n 
soc_top/components/cpu/core/datapath.vhm will rearrange the bytes how 
the cpu expects based on the lowest 2 bits of the address.

The rd and wr signals distinguish reads from writes. Together with the 
en signal, there is some redundant information that we may remove in 
future versions.

For a read en='1', rd='1', and wr='0'.
For a write en='1', rd='0', and wr='1'.

During a write, the cpu also sets db_o.d to the data to write, and 
db_o.we is the byte write enable. This is 4 flags controlling which 
bytes in the 32-bit value are written. Valid values for db_o.we are 
"1111", "1100", "0011", "1000", "0100", "0010", and "0001". For example, 
db_o.we(3) = '1' means that the db_o.d(31 downto 24) should be written.

As with an instruction fetch, after the cpu raises the en signal, it 
will latch the db_i.d and db_i.ack signals at rising clock edges, 
waiting to see db_i.ack='1'. If it's a read, then when db_i.ack='1' the 
cpu will take db_i.d as the read data.

> Any info on what signals the cpu uses, and how they are used, during a
> memory read/write would help immensely.

I hope the above explains that. An important point is that both of these 
buses support back-to-back operation. If the cpu raises the 'en' signal 
and your memory controller, or whatever is connected to the bus, raises 
'ack' in the same cycle, then the memory operation will complete in one 
cycle. In the next cycle the cpu may raise 'en' again, or leave it 
raised, to start another memory operation.

You mentioned data_bus array types. I guess you're referring to the 
types in the soc_top/targets/boards/*/devices.vhd files. These arrays 
are for splitting a single data bus (which is merged from buses 
connected to the cpus and dmac) and connecting it to all of the 
peripherals. I'd suggest starting with the bare minimum: a uart and an 
aic. If you can get that running on your board you'll be in a good spot 
to connect the memory controller.

If you're using soc_gen, the attached design.edn is one I just modified 
from soc_top/targets/boards/mimas_v2/design.edn by removing things. 
Running soc_gen with this should generate a simplified soc. I haven't 
tried building the result. You'll likely have to tweak the bootloader 
build by doing things like passing CONFIG_SDCARD=1 to the "make -C 
$(TOP_DIR)/boot" command when building boot.elf.

- Geoff
-------------- next part --------------
{:target :spartan6
 :plugins #import "../default_plugins.edn"
 :pins {:file "../pins/mimas_v2.pins"
        :type :pin-names
         ;; Default to LVCMOS33 for all pins and then override for
         ;; certain pins later
         {:match ".*" :attrs {:iostandard "LVCMOS33"}}

         {:match "clk_100mhz" :signal true :buff false}

         {:match "uart_tx" :signal "uart0_rx"}
         {:match "uart_rx" :signal "uart0_tx" :attrs {:drive 8 :slew "fast"}}
         {:match "spi_.*" :attrs {:drive 8 :slew "fast"}}
         ;; never select on board flash
         {:match "spi_cs" :out 1}

         ;; ddr pins
         {:match "mcb3_dram_.*" :attrs {:iostandard "MOBILE_DDR"}}
         ;; addresses
         {:match ["mcb3_dram_a" n] :signal ["ddr_sd_ctrl.a(" n ")"]}
         {:match ["mcb3_dram_ba" n] :signal ["ddr_sd_ctrl.ba(" n ")"]}
         ;; clock
         {:match "mcb3_dram_ck[_n]?" :attrs {:iostandard "DIFF_MOBILE_DDR"}}
         {:match "mcb3_dram_ck_n" :signal {:name "ddr_clk" :diff :neg}}
         {:match "mcb3_dram_ck" :signal {:name "ddr_clk" :diff :pos}}
         {:match "mcb3_dram_cke" :signal "ddr_sd_ctrl.cke"}
         ;; control
         {:match "mcb3_dram_cas_n" :signal "ddr_sd_ctrl.cas"}
         {:match "mcb3_dram_ras_n" :signal "ddr_sd_ctrl.ras"}
         {:match "mcb3_dram_we_n" :signal "ddr_sd_ctrl.we"}
         ;; data
         {:match ["mcb3_dram_dq" n]
          :in ["dr_data_i.dqi(" n ")"]
          :out ["dr_data_o.dqo(" n ")"]
          :out-en ["dr_data_o.dq_outen(" n ")"]}
         {:match "mcb3_dram_ldm"
          :out "dr_data_o.dmo(0)"
          :out-en "dr_data_o.dq_outen(16)"}
         {:match "mcb3_dram_udm"
          :out "dr_data_o.dmo(1)"
          :out-en "dr_data_o.dq_outen(17)"}
         {:match "mcb3_dram_ldqs"
          :in "dr_data_i.dqsi(0)"
          :out "dr_data_o.dqso(0)"
          :out-en "dr_data_o.dqs_outen(0)"}
         {:match "mcb3_dram_udqs"
          :in "dr_data_i.dqsi(1)"
          :out "dr_data_o.dqso(1)"
          :out-en "dr_data_o.dqs_outen(1)"}

         {:match ["dpswitch" n] :attrs {:pullup true}}
         {:match ["switch" n] :attrs {:pullup true}}
         {:match ["led" n] :attrs {:drive 24 :tig "yes"} :out ["po(" n ")"]}

         {:match "sd_miso" :signal "flash_miso"}
         {:match "sd_mosi" :signal "flash_mosi" :attrs {:drive 8 :slew "fast"}}
         {:match "sd_cs" :signal "flash_cs(0)" :attrs {:drive 8 :slew "fast"}}
         {:match "sd_clk" :signal "flash_clk" :attrs {:drive 8 :slew "fast"}}

         {:match ["sevensegment" n] :attrs {:drive 8 :tig "yes"}} ;; drive 24?
         {:match "sevensegment0" :out "po(8)"}
         {:match "sevensegment1" :out "po(9)"}
         {:match "sevensegment2" :out "po(10)"}
         {:match "sevensegment3" :out "po(11)"}
         {:match "sevensegment4" :out "po(12)"}
         {:match "sevensegment5" :out "po(13)"}
         {:match "sevensegment6" :out "po(14)"}
         {:match "sevensegment7" :out "po(15)"}
         {:match "sevensegmentenable0" :out "po(16)"}
         {:match "sevensegmentenable1" :out "po(17)"}
         {:match "sevensegmentenable2" :out "po(18)"}

         {:match "audio_.*" :attrs {:drive 8 :slew "fast"}}
         {:match "vga_.*" :attrs {:drive 8 :slew "fast"}}

         {:match ["io_p" n "_" m] :signal ["io_p" n "(" m ")"]
          :attrs {:drive 8 :slew "fast"}}]}

 ;; Some signals have different names in different blocks. This
 ;; setting tells the tool which signals are actually the same signal.
 ;; When entities are parsed, each signal in the list of signals on
 ;; the right is renamed to the signal on the left
 {"pll_rst" ["ddr_clkgen_reset_i" "pll_250_reset_o"]
  "clk_sys" ["clk_cpu" "clk_mem"]
  "clk_sys_90" ["clk_mem_90"]
  "clk_sys_2x" ["clk_mem_2x"]}

 ;; Supply a zero value for a list of global signals that aren't
 ;; output by devices in the design

 {"cpu1" false}

 {:data-bus-decode :exact
  :dram [0x10000000 0x4000000]}

 {$include "../soc.edn"}

 ;; list entities, other than top, that should be instantiated at the
 ;; padring level
 :padring-entities {"pll_250" {:generics {"CLK_CPU_DIVIDE" CFG_CLK_CPU_DIVIDE}
                               :ports {"clk" "clk_100mhz"
                                       "clk_cpu" "clk_sys"
                                       "locked" "clock_locked0"}}
                    "reset_gen" {:ports {"clock_locked1" 1}}}

 ;; list entities, other that the devices entity, that should be
 ;; instantiated at the top level
 :top-entities {"cpus" {:configuration "one_cpu_decode_rom_fpga"}}

 {$include "../common_device_classes.edn"}
 [{$include "../aic1_cpu0.edn"}
  {:class "uartlite"
   :name "uart0"
   :base-addr 0xabcd0100
   :irq 1
   :generics {"intcfg" 1
              "bps" 19.2e3}
   :dt-props {:current-speed (19200)
              :port-number (0)}
   :dt-stdout true}]}

More information about the J-core mailing list