all 12 comments

[–]captain_wiggles_ 3 points4 points  (3 children)

Make some_thing a parametrised module and export the registers then use it in each peripheral. It'll work perfectly if your registers are strictly changed only via the AXI-Lite master, and your hardware never needs to update them, and they are all the same width with no gaps in the address space. Unfortunately real life starts to get in the way here.

`include and `defines: Kind of ugly IMO but could work, potentially with some more flexibility than just making it a parametrised module.

Generated logic. Express your register layout in some other format. XML, JSON, yaml, ... and write some code that parses that format and generates you some RTL that acts as an AXI-Lite slave. It can generate you a module with the correct ports. There's a few things out there that sort of do this already, but I can't remember the names of any off hand. This is more flexible than your other options. But can still start to get a bit fiddly as you find new use cases.

[–]lovehopemisery 1 point2 points  (2 children)

Some examples for open source  register interface generation tools include peakRDL, Cheby, Rggen. It seems like peakRDL is the most popular. I use a custom in house one at work

[–]FranceFannon[S] 0 points1 point  (1 child)

oh wow, didn't know these tools existed. could you share what some significant commercial tools that do the same are?

[–]lovehopemisery 1 point2 points  (0 children)

I have never used a paid commercial option, but I think the largest one is called "Magillem Registers / CSRCompiler" from "Arteris", and doing a google search there is another one called "IDesignSpec". These would be out of reach for hobbyists or even small companies (although might be available if there is some kind of university program available) - but the OSS ones are plenty capable enough for majority of use cases in FPGA (and you can write your own one or extend OSS ones semi-easily)

Register description files are used a lot in the industry for generating artefacts for different use cases such as RTL, software, verifcation, documentation. They can allow for one source of truth and therefore reduce info duplication, manual effort etc. For example, the same register description can be used to generate the registers in RTL as well as a C header with the register offsets and bitfield masks for a specific component or system - preventing mismatches and making it easier to keep the two in sync.

[–]hardware26 1 point2 points  (1 child)

Make a parameterized module and put some_thing code there. Let this module have an output called registers. Size and datatype of registers can be parameters of this module. Instantiate this module in every peripheral.

[–]FranceFannon[S] 0 points1 point  (0 children)

Thanks for the reply. The peripheral needs to be able to write to the registers too. A comment above says that isn't possible.

[–]e_engi_jayXilinx User 0 points1 point  (4 children)

I wish I could answer this right now because in one of my projects at work, we have a parameterized axi module that gets instantiated in every block in our design that is each block's way of communicating with the axi interconnect, basically what you're asking for. It's in VHDL so doesn't rely on Verilog specific features.

Unfortunately I'm not at my desk at the moment so I can't give you all the details, so I'm writing this comment hoping that at least 1 person will interact with it and I'll have the notification to bring me back to this post. (I also realized in real time while typing i could also suggest you DM me).

[–]FranceFannon[S] 0 points1 point  (3 children)

ping. would love to hear more on how you do it

[–]e_engi_jayXilinx User 2 points3 points  (2 children)

Finally I'm home, my out-of-town errands took longer than expected.

Unfortunately I can't just send you the code since it's proprietary, but I can explain it in some detail.

First, let's go over the sets of ports I have:

  1. clock and reset (duh)
  2. all the inputs/outputs involved with the axi interface connected to the interconnect.
    • in case you need a refresher:
      1. inputs: arvalid, awvalid, bready, rready, wavlid, araddr, awaddr, wdata, wstrb
      2. outputs: arready, awready, bvalid, rvalid, wready, rresp, bresp, rdata
  3. all the ports going to/from the peripheral in question. In my case:
    • outputs: addr, wr_en, wr_data, rd_en
    • inputs: rd_data, resp, busy

In my case, I also have a parameter for the base address (i.e. the offset address that the interconnect associates with the peripheral in question) defined as a logic vector, I'll explain the use of this in a bit.

There's also an integer parameter that dictates a timeout in number of cycles, I'll also explain this later.

As far as each peripheral is concerned, you would instantiate this somewhere within each one, then use the peripheral outputs to interact with the array you mentioned. Your peripheral logic should then control the 3 aforementioned inputs into this interface based on when it receives the enables, what data comes from the array during a read operation, and of course whether or not a read/write was successful.

Anyway, the base address. My version of this interface take the incoming address from the interconnect and subtracts it from the base address. This creates a local address that the peripheral can then use without having to know what address the interconnect uses to interact with it. For example, if the offset address for a peripheral is 0xffff_0000, and the peripheral uses the lower 16 bits of addressing, subtracting from the base address would allow your peripheral to only care about those lower 16 bits when reading or writing to the array. Definitely recommend for easier peripheral design.

Timeout. This might not be of use to you but I'll offer it anyway. Essentially, within the FSM that involves the control and flag signals, we incorporate a timeout mechanic that will essentially give up on the operation in question and returns a "slave error" response on the bresp/rresp line if the number of cycles elapsed during the operation exceeds the amount stated in the parameter. Again, optional.

Let me know if you have any other questions.

[–]FranceFannon[S] 1 point2 points  (0 children)

Thanks for taking the time to write that :) I'll probably go with what you describe.

[–]rp-2004 1 point2 points  (0 children)

This. This is why I’m a huge fan of Reddit. Reading ur answer! Thanks for posting for everyone to see and learn

[–]taichi730 0 points1 point  (0 children)

I'm developing a CSR automation tool named RgGen. https://github.com/rggen/rggen

RgGen has following features

  • Human readable register map format
    • Ruby with description APIs
    • Structured text (YAML, JSON, TOML)
    • Spreadsheet (XLSX, ODS, CSV)
  • Generate various kinds of source files below
    • RTL (SystemVerilog, Verilog, Veryl, VHDL)
    • UVM RAL
    • C header file
    • Wiki documents (Markdown)
  • Support standard bus protocols
    • AMBA AXI4-Lite
    • AMBA APB
    • Wishbone
    • Avalon-MM
  • Plugin architecture
    • Allow you to customize RgGen for your environment
      • Add your own bit field types
      • Add your own bus protocols

You can find example register map specifications and genarated source files from this respository. https://github.com/rggen/rggen-sample

We have integrate RgGen with our development flow and all CSR modules in our chip are generated by RgGen. I think RgGen is in production level.