This was a weekend project back in 2020 to build IPXACT parser and UVM register Model generator. As an example of the lack of imagination at that time, I decided to call it ‘ipaxctral’. Creative?! I know!
IPXACT Link to heading
I will just copy/paste the wiki here. But full Spec is on Accellera website
IP-XACT is an XML format that defines and describes individual, re-usable electronic circuit designs (individual pieces of intellectual property, or IPs) to facilitate their use in creating integrated circuits (i.e. microchips).
The important section is <ipxact:register>
as it specifies the registers and fields.
UVM RAL Link to heading
Well, if you haven’t seen Register model before, I envy you. You had a good life so far. I won’t dive deep into UVM RAL here. I will just put this from UVM user guide
The UVM register layer classes are used to create a high-level, object-oriented model for memory-mapped registers and memories in a design under verification (DUV)
Basically, Once fields and registers are defined, RAL provides a consistent access API for registers read/write.
class foo_csr extends uvm_reg;
rand uvm_reg_field enable;
rand uvm_reg_field ID;
endclass
Design and Implementation Link to heading
The plan was simple:
- Use
xml.etree
to parse IPXACT XML and extract registers and fields - Build dict for jinja2
context
- Write Jinja2 templates to consume the context.
Easy enought. Right? I thought so. But XML is really really bad format to iterate. Anyway, I finally managed to build my own tree(nested dict really).
for reg in addressBlock.findall("ipxact:register", ns):
reg_context = {}
block_context["registers"].append(reg_context)
reg_context["name"] = reg.find("ipxact:name", ns).text
reg_context["size"] = reg.find("ipxact:size", ns).text
reg_context["addressOffset"] = reg.find(
"ipxact:addressOffset", ns
).text
reg_context["fields"] = []
for field in reg.findall("ipxact:field", ns):
field_context = {}
reg_context["fields"].append(field_context)
field_context["name"] = field.find("ipxact:name", ns).text
field_context["description"] = field.find(
"ipxact:description", ns
).text
field_context["bitOffset"] = field.find(
"ipxact:bitOffset", ns
).text
field_context["bitWidth"] = field.find(
"ipxact:bitWidth", ns
).text
field_context["access"] = access2short(
field.find("ipxact:access", ns).text
)
field_context["reset"] = (
field.find("ipxact:resets", ns)
.find("ipxact:reset", ns)
.find("ipxact:value", ns)
.text
)
Once I have that working, I can pass the contexts
to Jinja to render.
reg_template = self.jinja_env.get_template("reg.sv.jinja")
txt = reg_template.render(context=reg_context)
print(txt)
block_template = self.jinja_env.get_template("reg_block.sv.jinja")
txt = block_template.render(context=block_context)
print(txt)
The last step is to write the templates to consume the context.
class {{ context["name"]}} extends uvm_reg;
{% for field in context["fields"] %}
rand uvm_reg_field {{ field["name"] }};
{% endfor %}
function new (string name = "{{ context["name"] }}" );
super.new (name, {{ context.size }} , UVM_NO_COVERAGE);
endfunction
virtual function void build ();
{% for field in context["fields"] %}
this.{{ field["name"] }} = uvm_reg_field::type_id::create ("{{ field["name"] }}");
this.{{ field["name"] }}.configure (this,
{{ field["bitWidth"] }},
0,
"{{ field["access"] }}",
0,
1'h0,
1,
1,
1);
{% endfor %}
endfunction
endclass
PS. I made the template flexible enough by passing it on CLI. But what I really wanted was to define a better data structure (context API) for Jinja2 to consume. Well, That’s why i am reviving it :)