Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APB3 to AXI Stream peripheral to Murax (with FIFO) #53

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

eine
Copy link

@eine eine commented Jan 23, 2019

This PR is an effort to add AXI Stream interfaces to Murax SoC. The scheme of the resulting design is as follows:

JTAG <-> RISCV <-> AxiCrossbar <-> OnChipRam
                               <-> APB3Bridge <-> GPIO <------------------> gpioA
                                              <-> Timer
                                              <-> UartCtrl <--------------> uart
                                              <-> Apb3Axis <- StreamFifo <- axis_input
                                                           -> StreamFifo -> axis_output

The example software to test the peripheral is the following:

Based on https://github.com/SpinalHDL/VexRiscvSocSoftware/blob/master/projects/murax/demo/src/main.c

#include <stdint.h>
#include <murax.h>

void main() {
  interruptCtrl_init(TIMER_INTERRUPT);
  prescaler_init(TIMER_PRESCALER);
  timer_init(TIMER_A);

  TIMER_PRESCALER->LIMIT = 12000-1; //1 ms rate

  TIMER_A->LIMIT = 10-1;  //1 second rate
  TIMER_A->CLEARS_TICKS = 0x00010002;

  TIMER_INTERRUPT->PENDINGS = 0xF;
  TIMER_INTERRUPT->MASKS = 0x1;

  GPIO_A->OUTPUT_ENABLE = 0x000000FF;
  GPIO_A->OUTPUT = 0x00000000;

  UART->STATUS = 2; //Enable RX interrupts
  UART->DATA = 'A';

  while(1){
    while(AXIS->IN_VALID==0) { asm volatile(""); }
    AXIS->OUT_DATA = 3 * AXIS->IN_DATA;
    AXIS->OUT_VALID = 0xFFFF;
    while(AXIS->OUT_VALID!=0) { asm volatile(""); }
    AXIS->IN_READY = 0xFFFF;
    while(AXIS->IN_READY!=0) { asm volatile(""); }
  }

}

void irqCallback(){
  if(TIMER_INTERRUPT->PENDINGS & 1){  //Timer A interrupt
    GPIO_A->OUTPUT ^= 0x80; //Toogle led 7
    TIMER_INTERRUPT->PENDINGS = 1;
  }
  while(UART->STATUS & (1 << 9)){ //UART RX interrupt
    UART->DATA = (UART->DATA) & 0xFF;
  }
}

Based on https://github.com/SpinalHDL/VexRiscvSocSoftware/blob/master/projects/murax/libs/murax.h

/*
 * briey.h
 *
 *  Created on: Aug 24, 2016
 *      Author: clp
 */

#ifndef BRIEY_H_
#define BRIEY_H_

#include "timer.h"
#include "prescaler.h"
#include "interrupt.h"
#include "gpio.h"
#include "uart.h"
#include "axis.h"

#define CORE_HZ 12000000

#define GPIO_A          ((Gpio_Reg*)(0xF0000000))
#define TIMER_PRESCALER ((Prescaler_Reg*)0xF0020000)
#define TIMER_INTERRUPT ((InterruptCtrl_Reg*)0xF0020010)
#define TIMER_A         ((Timer_Reg*)0xF0020040)
#define TIMER_B         ((Timer_Reg*)0xF0020050)
#define UART            ((Uart_Reg*)(0xF0010000))
#define UART_SAMPLE_PER_BAUD 5
#define AXIS            ((AXIS_Reg*)(0xF0030000))

#endif /* BRIEY_H_ */

New axis.h file added to https://github.com/SpinalHDL/VexRiscvSocSoftware/tree/master/libs:

#ifndef AXIS_H_
#define AXIS_H_

typedef struct {
  volatile uint32_t IN_DATA;
  volatile uint32_t IN_READY;
  volatile uint32_t IN_VALID;
  volatile uint32_t OUT_DATA;
  volatile uint32_t OUT_VALID;
  volatile uint32_t OUT_READY;
} AXIS_Reg;

#endif /* AXIS_H_ */

The objective of this extension is to support:

  • Making Murax the coprocessor for an ARM device in SoC such as Zynq.
  • Adding a coprocessing pipeline to Murax.

This PR was tested in a VUnit testbench with AXI Stream Verification Components. Precisely, the FIFO in array_axis_vcs was replaced with the VHDL generated with SpinalHDL. GHDL was used for simulation.


Although it is functional, I feel that this PR could be significantly improved. The current procedure for a read transaction is as follows:

  • The RISCV core is waiting for AXIS->IN_VALID to be true/high.
  • The FIFO between axis_input and the APB3 ctrl is empty, so axis_input.ready is true.
  • Some peripheral writes a value to axis_input, i.e. valid data is presented in axis_input.data and axis_input.valid is driven high for a clock period.
  • Since the FIFO is not empty anymore, the RISCV core reads AXIS->IN_VALID to be true. Then, the content is read from AXIS->IN_DATA.
  • However, the FIFO does not know that the data was read, so AXIS->IN_VALID is still true. Therefore, the RISCV core drives AXIS->IN_READY to true. As defined in class Apb3Axis, as soon as ready is driven to true, it is reset by hardware due to valid being true. At the same time, valid is driven to false by the StreamFifo, because ready and valid where true during a clock cycle.

On the one hand, this does not fulfill the specification, because a slave should drive ready to true when it is ready to receive some data, without waiting for valid. Also, data should be read when both ready and valid are true. Fortunately, this is only an issue between the RISCV core and StreamFifos; external interfaces are correct. It would be a problem if FIFOs where removed, in order to provide unbuffered streams.

On the other hand, I wonder if I am missing some software libraries that would allow me to handle reading from and writing to StreamFifo as read/writes to memory-mapped registers. Ideally, this would be supported:

uint32_t * AXIS = (uint32_t *)0xF0030000;
*AXIS = 3 * *AXIS;

Note that in this example both reads and writes would be blocking. In order to support non-blocking transactions an additional register would be needed to display the status of both streams.


Overall, I think that the two issues described above are constrainted by the actual implementation. These alternatives for some of the new parts in Murax.scala where suggested in gitter.

  • Replace lines 176-182 with:
ctrl.readStreamNonBlocking(io.input.queue(128), address = 0)
  • Replace lines 187-190 with:
val writeFlow = ctrl.createAndDriveFlow(Bits(32  bits), address = 0)
writeFlow.toStream.stage() >> ofifo.io.push

However, I could not guess the software procedures to drive them. Moreover, I wonder whether ctrl.writeStreamNonBlocking(io.input.queue(128), address = 4) could exist.

@Dolu1990
Copy link
Member

Ahhh damned i completly forgot that pull request. Just ping me when i forgot things :)

I can't check it right now, will do this evening.

@Dolu1990
Copy link
Member

I have two issues.

  1. Is the implementation of the Apb3AxiStream, Streams valid and ready shoudn't be mapped like a GPIO, but be more like a transaction based thing. Writing in a register should push a transaction on the stream, reading a register should pull something from a stream, and that's all.
  2. integration into Murax. Basicaly, i try to keep murax as simple as posslbe, with a very basic set of features. I added XIP in it for the demo, but i shoudn't have done that, as it complexify the code base. The goal of Murax is realy to give a demo.
    But i'm working now on Saxon, which should provide a user friendly way of adding feature into the SoC without having to fork or modifying the Soc toplevel, a little bit like VexRiscv is done via the plugin system. It is work in progress in the Saxon repository :)

Have you seen it ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants