-
Couldn't load subscription status.
- Fork 76
feat: add GPR Information to UDB #1150
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
base: main
Are you sure you want to change the base?
feat: add GPR Information to UDB #1150
Conversation
Implements General Purpose Register (GPR) support in the Unified Database as requested in issue riscv-software-src#1085. Changes: - spec/schemas/register_schema.json: New schema defining structure for register files - spec/schemas/schema_defs.json: Add register-specific schema definition - spec/std/isa/register/gpr.yaml: New register file implementing all 32 RISC-V general purpose registers with proper ABI mnemonics, calling convention roles, and conditional support for RV32E - spec/std/isa/README.adoc: Update architecture documentation to include register files - tools/ruby-gems/udb/lib/udb/obj/register_file.rb: New RegisterFile class extending TopLevelDatabaseObject - tools/ruby-gems/udb/lib/udb/obj/database_obj.rb: Add RegisterFile kind to DatabaseObject::Kind enum for type system integration - tools/ruby-gems/udb/lib/udb/architecture.rb: Add register file loading support to architecture The implementation follows RISC-V ABI specifications and provides a single source of truth for GPR information. Resolves Issue: riscv-software-src#1085
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is, frankly, pretty awesome! Great work, @AnimeshAgarwal28 !
There are a few comments that need discussion, but this is an impressive start!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent first draft!
In addition to the inline comments, a few high-level thoughts:
To really get this integrated, we'll want to reflect it in a few places where the X registers are built in concepts. A few that come to mind: the IDL symbol table and the generated ISS.
In the symbol table, you'll find this:
riscv-unified-db/tools/ruby-gems/idlc/lib/idlc/symbol_table.rb
Lines 221 to 226 in 5d6c397
| @scopes = [{ | |
| "X" => Var.new( | |
| "X", | |
| Type.new(:array, sub_type: XregType.new(@mxlen.nil? ? 64 : @mxlen), width: 32, qualifiers: [:global]) | |
| ), | |
| "XReg" => XregType.new(@mxlen.nil? ? 64 : @mxlen), |
That's where "X" gets added in global scope as an array of Bits. Instead of that, we'll want to construct (all of) the register files based on the YAML. Hopefully you can get some ideas of to do so looking around here where the symbol table is instantiated:
riscv-unified-db/tools/ruby-gems/udb/lib/udb/cfg_arch.rb
Lines 462 to 488 in 5d6c397
| @symtab = | |
| Idl::SymbolTable.new( | |
| mxlen:, | |
| possible_xlens:, | |
| params:, | |
| builtin_funcs: symtab_callbacks, | |
| builtin_enums: [ | |
| Idl::SymbolTable::EnumDef.new( | |
| name: "ExtensionName", | |
| element_values: (1..extensions.size).to_a, | |
| element_names: extensions.map(&:name) | |
| ), | |
| Idl::SymbolTable::EnumDef.new( | |
| name: "ExceptionCode", | |
| element_values: exception_codes.map(&:num), | |
| element_names: exception_codes.map(&:var) | |
| ), | |
| Idl::SymbolTable::EnumDef.new( | |
| name: "InterruptCode", | |
| element_values: interrupt_codes.map(&:num), | |
| element_names: interrupt_codes.map(&:var) | |
| ) | |
| ], | |
| name: @name, | |
| csrs: | |
| ) | |
| overlay_path = config.info.overlay_path |
In the ISS, the X registers are defined on a "hart" object, as you can see here:
riscv-unified-db/backends/cpp_hart_gen/templates/hart.hxx.erb
Lines 378 to 415 in 5d6c397
| uint64_t xreg(unsigned num) const override { | |
| if (num >= 32) { | |
| throw std::out_of_range("X register indices are 0 - 31, inclusive"); | |
| } | |
| return _xreg(num).get(); | |
| } | |
| PossiblyUnknownBits<MXLEN> _xreg(unsigned num) const { | |
| return m_xregs[num]; | |
| } | |
| template <template <unsigned, bool> class BitsClass, unsigned N, bool Signed> | |
| requires (BitsType<BitsClass<N, Signed>>) | |
| PossiblyUnknownBits<MXLEN> _xreg(const BitsClass<N, Signed>& num) const { | |
| return m_xregs[num.get()]; | |
| } | |
| // XRegister<MXLEN>& xregRef(unsigned num) { return m_xregs[num]; } | |
| void set_xreg(unsigned num, uint64_t value) override { | |
| if (num >= 32) { | |
| throw std::out_of_range("X register indices are 0 - 31, inclusive"); | |
| } | |
| _set_xreg(Bits<8>{num}, Bits<MXLEN>{value}); | |
| } | |
| template < | |
| template <unsigned, bool> class IdxType, unsigned IdxN, bool IdxSigned, | |
| template<unsigned, bool> class ValueType, unsigned ValueN, bool ValueSigned | |
| > | |
| requires (BitsType<IdxType<IdxN, IdxSigned>> && BitsType<ValueType<ValueN, ValueSigned>>) | |
| void _set_xreg(const IdxType<IdxN, IdxSigned>& num, const ValueType<ValueN, ValueSigned>& value) { | |
| if (num != 0_b) { | |
| m_xregs[static_cast<unsigned>(num.get())] = value; | |
| } | |
| } | |
riscv-unified-db/backends/cpp_hart_gen/templates/hart.hxx.erb
Lines 529 to 534 in 5d6c397
| <%- if cfg_arch.mxlen.nil? -%> | |
| std::array<PossiblyUnknownRuntimeBits<64>, 32> m_xregs; | |
| <%- else -%> | |
| std::array<PossiblyUnknownBits<MXLEN>, 32> m_xregs; | |
| <%- end -%> | |
We'll want to think this one through a bit more with @henrikg-qc
| when: | ||
| not: { name: E } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After #891, this would be:
when:
not:
extension:
name: EChanges: - rename schema from register_schema.json to register_file_schema.json, remove "$ref": "#/$defs/register_file" from bottom of the schema, and introduce register_file_name. - drop per-register length and index fields, make abi mnemonics an array, and add 'caller_saved'/'callee_saved' booleans with default value of 'false'. - remove the 'count' helper in favor of conditioning individual register entries directly and infer indices from position. - fix the description for XLEN behavior and remove empty role arrays. - update the register-file section of README.adoc so the example mirrors the new schema. - register_file.rb: expose register data via '#data' and return Sorbet enum values for roles. Signed-off-by: Animesh Agarwal <animeshagarwal28@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put this in the directory spec/std/isa/register_file.
| long_name: General Purpose Registers | ||
| description: | | ||
| The 'X' register file contains the general-purpose integer registers. Each register | ||
| is architecturally XLEN bits wide, unless XLEN<MXLEN. Register 'x0' is hardwired to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This says "A unless B", but doesn't say what "B" implies.
Should we say "Each register is architecturally MXLEN bits wide"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's go with "Each register is MXLEN bits wide".
| sw_read(): | | ||
| return 0; | ||
| sw_write(value): | | ||
| # x0 ignores all writes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sw_write() is expected to return the value to be written, so this needs a return 0.
Alternatively, we could add a writable attribute as is done for CSRs, so you wouldn't need a sw_write() method for read-only registers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the alternative approach of adding a writable attribute, makes it explicit.
| roles: [callee_saved, frame_pointer] | ||
| ---- | ||
|
|
||
| <1> Registers can have either a fixed architecture width or take their width from a parameter (e.g., MXLEN, VLEN). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose expanding this a tad, just to help make it more obvious that every register in a register file is the same width:
The registers in a register file have identical fixed widths, either an explicit width or from a parameter (e.g. MXLEN or VLEN).
... or something like that.
| }, | ||
| "register_file_name": { | ||
| "type": "string", | ||
| "pattern": "^[A-Za-z][A-Za-z0-9_.-]*$", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's restrict this to a single character. Since we already have X and f, either case:
| "pattern": "^[A-Za-z][A-Za-z0-9_.-]*$", | |
| "pattern": "^[A-Za-z]$", |
|
Interesting... the CI is complaining:
I'm not sure exactly who's complaining about that, but I guess we should rename the file to |
This PR implements GPR definition in the Unified Database, addressing issue #1085.
Problem
The UDB currently lacks information about General Purpose Registers in YAML format.
Solution
This PR adds structured register file support to UDB, starting with RISC-V general purpose registers as a foundation for future register file additions.
Changes
New Files
spec/schemas/register_schema.json: JSON schema defining the structure for YAML register file.spec/std/isa/register/gpr.yaml: Complete RISC-V general purpose register file with all 32 registers, proper ABI mnemonics, calling convention roles, and conditional support for RV32E (16 registers)tools/ruby-gems/udb/lib/udb/obj/register_file.rb: RegisterFile class extending TopLevelDatabaseObject for programmatic access to register file dataModified Files
spec/schemas/schema_defs.json: Added register-specific schema definitionsspec/std/isa/README.adoc: Updated architecture documentation to include register files alongside extensions/instructions/CSRs with usage examplestools/ruby-gems/udb/lib/udb/obj/database_obj.rb: Added RegisterFile kind to DatabaseObject::Kind enum for type system integrationtools/ruby-gems/udb/lib/udb/architecture.rb: Added register file loading support to architecture systemRegister Details
The GPR implementation includes:
Benefits
Future Work
This establishes the foundation for adding other register files mentioned in issue #1085:
Closes #1085