Starting from:

$25

EE2003-Assignment 6 I/O Interfacing Solved

Implement aa simple output peripheral and use it to display text and numbers


At this point (after A5) you should have a complete working single cycle peripheral that is capable of executing all the RV32I instructions (barring possibly the `fence` instructions).  Now you need to create a simple peripheral, and memory map it to a suitable location.

Your peripheral will need to respond to read/write requests on specific addresses, and for this, you should choose addresses that are NOT part of the DMEM.  The `biu` or *Bus Interface Unit* module is responsible for taking the addresses sent by the CPU and decoding whether they are meant for the DMEM or the peripheral.  You can choose addresses as you like, but will need to update them in the C code as well as the Verilog.

### Memory Mapped Read/Write 

The peripheral you will create will be *memory mapped* - this is done by part of the decoding in the BIU, so that the peripheral itself does not care at which address it is placed.  Only the BIU and the C code need to be aware of this.

The C code itself can then directly read or write, but needs to know which address to deal with.  An example is given in the `_outbyte` function in the sample code:

```c
#define OUTPERIPH_BASE 0x34560
#define OUTPERIPH_WRITE_OFFSET 0x00
#define OUTPERIPH_READSTATUS_OFFSET 0x04
void _outbyte(int c)
{
        int *p;  // Pointer to integer
        p = (OUTPERIPH_BASE + OUTPERIPH_WRITE_OFFSET); // Set pointer value directly
        (*p) = c; // Write the value to the address
}
```

The basic idea in the above code is that we make clever use of *pointers* in C: a pointer is just a regular integer-like value that contains an address.  In our case, we directly store the address (`0x34560` in our case - this is a completely arbitrary choice - you just need to make sure it is outside the range of DMEM, otherwise the BIU may make parts of the DMEM inaccessible to the CPU, or the peripheral may not be accessible (memory shadowing)).

The generated code is:

```asm
00000018 <_outbyte:
  18:   fd010113                addi    x2,x2,-48
  1c:   02812623                sw      x8,44(x2)
  20:   03010413                addi    x8,x2,48
  24:   fca42e23                sw      x10,-36(x8)
  28:   000347b7                lui     x15,0x34
  2c:   56078793                addi    x15,x15,1376 # 34560 <__global_pointer$+0x323ac
  30:   fef42623                sw      x15,-20(x8)
  34:   fec42783                lw      x15,-20(x8)
  38:   fdc42703                lw      x14,-36(x8)
  3c:   00e7a023                sw      x14,0(x15)
  40:   00000013                addi    x0,x0,0
  44:   02c12403                lw      x8,44(x2)
  48:   03010113                addi    x2,x2,48
  4c:   00008067                jalr    x0,0(x1)
```

As can be seen, the address (`0x34560`) gets loaded into `x15` through a combination of `lui` and `addi`.  Once that is done, the `sw` instruction on line `3c` can be used to write the value into the peripheral.  Similar code, but reading from the pointer, can be used to read back the status 

More products