Memory and I/O maps, soft reset, and interrupts

Note: the content of this section is subject to change as the specification develops.

This untethered release starts to regulate the shared resources among cores, such as interrupts, memory and I/O maps, global timers, etc. A subset of control status register (CSR) space is defined as processor control registers (PCRs), whose values and accesses are shared by all cores and controlled by a global PCR control unit (PCRControl). The connections of the PCR control units is shown below.


Ports of PCRControl

PCR read/write requests (pcr_req)

The CSR file in each core has a dedicated request channel to the global PCR control unit. When a CSR address is identified as a PCR, the CSR request is forwarded to PCRControl.

PCR read/write response (pcr_resp)

Upon receiving a request from pcr_req, the PCR control unit processes the request and sends back a response through the pcr_resp port connected to the requesting core when it is finished. When it is a read request, the PCR value is sent back in the response.

Broadcast of PCR update (pcr_update)

Operations on some PCRs trigger changes of the global status, such as changing the I/O map. In this case, the updated PCR is broadcast to all components potentially affected by this change. In this implementation, every L1 D$ has an ioaddr module for identifying I/O requests. To enforce the address mapping defined in the memory map, a memory address converter (conv) is added just below the L2 arbiter. Besides CSR modules, all ioaddr modules and the memory address converter receive PCR updates to track the changes in I/O and memory maps.

IRQ (irq)

All cores share the same interrupt sources but they can enable/disable individual interrupts separately. When an interrupt arrives, it is forwarded to all cores who have enabled it.

Soft reset (soft_reset)

When a soft reset is triggered (a write to CSR reset), a reset signal is broadcast to all cores and the L2.

Individual PCRs

All PCRs are readable or writable in machine mode ONLY.

NameAddressOperationReset valueDescription
time0x701Read Only0Global wall clock.
tohost0x780Read/Write0Legacy, only used in ISA regression test to identify return value.
fromhost0x781Read/Write0Legacy, not used.
reset0x782Read/Write0When written, trigger a soft reset. Always reads 0.
mem_base00x7a0Read/WriteInitMemBaseBase address of memory section 0.
mem_mask00x7a1Read/writeInitMemMaskAddress mask of memory section 0.
mem_phy00x7a2Read/WriteInitPhyBasePhysical base address of memory section 0.
mem_base10x7a4Read/Write0Base address of memory section 1.
mem_mask10x7a5Read/write0Address mask of memory section 1.
mem_phy10x7a6Read/Write0Physical base address of memory section 1.
mem_base20x7a8Read/Write0Base address of memory section 2.
mem_mask20x7a9Read/write0Address mask of memory section 2.
mem_phy20x7aaRead/Write0Physical base address of memory section 2.
mem_base30x7acRead/Write0Base address of memory section 3.
mem_mask30x7adRead/write0Address mask of memory section 3.
mem_phy30x7aeRead/Write0Physical base address of memory section 3.
mem_update0x7afRead/Write0When written, trigger memory map update. Always reads 0.
io_base00x7b0Read/WriteInitIOBaseBase address of I/O section 0.
io_mask00x7b1Read/writeInitIOMaskAddress mask of I/O section 0.
io_base10x7b4Read/Write0Base address of I/O section 1.
io_mask10x7b5Read/write0Address mask of I/O section 1.
io_base20x7b8Read/Write0Base address of I/O section 2.
io_mask20x7b9Read/write0Address mask of I/O section 2.
io_base30x7bcRead/Write0Base address of I/O section 3.
io_mask30x7bdRead/write0Address mask of I/O section 3.
io_update0x7bfRead/Write0When written, trigger I/O map update. Always reads 0.
int_en00x7c0Read/Write0IRQ enable for core 0.
int_pending00x7c1Read OnlyN/APending IRQ for core 0.
int_en10x7c2Read/Write0IRQ enable for core 1.
int_pending10x7c3Read OnlyN/APending IRQ for core 1.
int_en20x7c4Read/Write0IRQ enable for core 2.
int_pending20x7c5Read OnlyN/APending IRQ for core 2.
int_en30x7c6Read/Write0IRQ enable for core 3.
int_pending30x7c7Read OnlyN/APending IRQ for core 3.
int_en40x7c8Read/Write0IRQ enable for core 4.
int_pending40x7c9Read OnlyN/APending IRQ for core 4.
int_en50x7caRead/Write0IRQ enable for core 5.
int_pending50x7cbRead OnlyN/APending IRQ for core 5.
int_en60x7ccRead/Write0IRQ enable for core 6.
int_pending60x7cdRead OnlyN/APending IRQ for core 6.
int_en70x7ceRead/Write0IRQ enable for core 7.
int_pending70x7cfRead OnlyN/APending IRQ for core 7.


The current wall clock counts at 50MHz. The value of this wall clock is updated to all cores every 20 cycles. The wall clock is not writable.

When a core reads the CSR time, an actual read of the wall clock is initiated. However, the timer comparator in each core is compared against the infrequently updated local copy, which incurs a 20 cycle inaccuracy in the worst case.

To/from host

This pair of registers are kept for legacy reasons. They are used in the ISA regression test only for identifying the end of a test and the return value. Writing tohost has no effect in FPGA but writing a non-zero value to fromhost triggers an exception.


Writing any value to reset triggers a soft reset.

Memory map

This implementation supports up to 4 separate memory sections. The space of any two sections should not overlap.

For each section, mem_base defines the base address as seen by the core; mem_mask defines the actual size of the section; mem_phy defines the base address as seen by on-chip interconnects. When mem_mask is 0, the section is disabled (size of 0). For any address (addr), it belongs to a memory section if (addr & ~mem_mask) == mem_base. The translated address to on-chip interconnects is (addr & mem_mask) | mem_phy.

The update of a memory section should be an atomic operation. To ease this requirement, any write to a memory map is buffered. The actual update to the memory map is triggered by a write to mem_update.

I/O map

This implementation supports up to 4 separate I/O sections. The space of any two sections should not overlap.

For each section, io_base defines the base address; io_mask defines the actual size of the section. When io_mask is 0, the section is disabled (size of 0). For any address (addr), it belongs to an I/O section if (addr & ~io_mask) == io_base. There is no address translation for I/O addresses.

Similar to the memory map, the update of an I/O section should be an atomic operation. In the same way, any write to I/O map is buffered. The actual update is triggered by a write to io_update.


Up to 64 interrupt sources are supported.

Every core has two PCRs: int_en and int_pending. int_en defines which IRQ should be notified to the core, while int_pending identifies any pending IRQs. The actual interrupt sources are latched in the PCR control unit. The value of int_pending is generated by int_pending = interrupt & int_en.

When an IRQ is triggered, the responding core can find out the actual IRQ source by reading int_pending. Currently the responding core needs to access the actual peripheral to resolve a pending IRQ.

In this implementation, interrupt bit 0 is connected to UART and bit 1 is connected to SPI.