@@ -28,8 +28,9 @@ into an array of single characters, each representing one bit.
2828Returns null if the string isn’t 16 or 32 bits long.
2929*/
3030function parseMatchBits ( matchStr ) {
31- if ( ! matchStr ) return null ;
32- if ( matchStr . length !== 32 && matchStr . length !== 16 ) return null ;
31+ if ( matchStr . length !== 16 && matchStr . length !== 32 ) {
32+ throw new Error ( `Invalid match string length ${ len } ; expected 16 or 32` ) ;
33+ }
3334 return matchStr . split ( "" ) ;
3435}
3536
@@ -42,26 +43,21 @@ Each segment defines one contiguous region of bits, and fields can have
4243multiple disjoint segments (like immediates in S- or B-type instructions).
4344*/
4445function parseLocationSegments ( loc ) {
45- if ( loc === undefined || loc === null ) return [ ] ;
4646 return String ( loc )
4747 . split ( "|" )
4848 . map ( part => part . trim ( ) )
49- . filter ( Boolean )
5049 . map ( part => {
5150 const [ hiStr , loStr ] = part . split ( "-" ) . map ( n => n . trim ( ) ) ;
52- const hi = parseInt ( hiStr , 10 ) ;
53- const lo = loStr !== undefined && loStr !== "" ? parseInt ( loStr , 10 ) : hi ;
54- if ( Number . isNaN ( hi ) || Number . isNaN ( lo ) ) return null ;
51+ const hi = parseInt ( hiStr ) ;
52+ // If no low end is provided (e.g., "31" or "31-"), treat it as a single-bit range at `hi`.
53+ const lo = loStr !== undefined && loStr !== "" ? parseInt ( loStr ) : hi ;
5554 return {
56- from : Math . max ( hi , lo ) ,
57- to : Math . min ( hi , lo )
55+ from : hi ,
56+ to : lo
5857 } ;
5958 } )
60- . filter ( Boolean )
61- . sort ( ( a , b ) => ( b . from - a . from ) || ( b . to - a . to ) ) ;
6259}
6360
64-
6561/*
6662Extract all variable and constant fields from an instruction definition.
6763
@@ -74,15 +70,19 @@ This function handles both flat encodings and multi-variant encodings
7470 ...
7571 ]
7672
77- These field objects describe what appears at each bit position.
73+ These field objects describe what appears at each bit position.
74+ The `kind` property indicates whether the field is a variable (part of the instruction encoding)
75+ or a constant (fixed bits defined by the match pattern).
76+ Segments are included for multi-segment fields such as when the location is non-contiguous.
7877*/
7978function computeFields ( doc ) {
79+ // Some instruction docs don't define an encoding; without match/variables we have no bit layout to emit.
8080 const fields = [ ] ;
8181 if ( ! doc . encoding ) return fields ;
82-
82+ // Encodings may be a flat object with match string/variables or a map of variants (e.g., { RV32: {...}, RV64: {...} });
8383 let enc = doc . encoding ;
8484 if ( ! enc . match && ! enc . variables ) {
85- // pick RV32 first, otherwise first available sub-encoding
85+ // pick RV32 first, otherwise first available sub-encoding (not all instructions have both RV32 and RV64)
8686 const variants = Object . keys ( enc ) ; // e.g., ["RV32", "RV64"]
8787 const chosenKey = variants . includes ( "RV32" ) ? "RV32" : variants [ 0 ] ;
8888 enc = enc [ chosenKey ] || { } ;
@@ -93,8 +93,19 @@ function computeFields(doc) {
9393
9494 // --- Parse variable fields (e.g., rd, rs1, imm) ---
9595 for ( const v of vars ) {
96- const segments = parseLocationSegments ( v . location ) ;
97- if ( segments . length === 0 ) continue ;
96+ const segments = parseLocationSegments ( v . location ) ; //not all variables have multiple locations
97+
98+ // ensure segments are sorted MSB->LSB and non-overlapping
99+ const sorted = [ ...segments ] . sort ( ( a , b ) => b . from - a . from || b . to - a . to ) ;
100+ for ( let i = 1 ; i < sorted . length ; i ++ ) {
101+ const prev = sorted [ i - 1 ] ;
102+ const curr = sorted [ i ] ;
103+ const overlaps = Math . max ( prev . to , curr . to ) <= Math . min ( prev . from , curr . from ) ;
104+ if ( overlaps ) {
105+ throw new Error ( `Overlapping segments for ${ v . name } : ${ JSON . stringify ( segments ) } ` ) ;
106+ }
107+ }
108+
98109 // Find the overall high/low bits and width
99110 const from = Math . max ( ...segments . map ( s => s . from ) ) ;
100111 const to = Math . min ( ...segments . map ( s => s . to ) ) ;
@@ -105,13 +116,17 @@ function computeFields(doc) {
105116 // --- Parse constant bit regions from the match pattern ---
106117 // (bits that are fixed 0/1, not "-")
107118 if ( match ) {
108- let bit = match . length - 1 ;
109- while ( bit >= 0 ) {
119+ for ( let bit = match . length - 1 ; bit >= 0 ; bit -- ) {
120+ if ( match [ match . length - 1 - bit ] === "-" ) continue ;
121+
122+ //For a pattern like "0000000-----000-----0110011", this extracts regions such as:
123+ // "0110011" at bits [6:0]
124+ // "000" at bits [14:12]
125+ // "0000000" at bits [31:25]
110126 const hi = bit ;
111127 while ( bit >= 0 && match [ match . length - 1 - bit ] !== "-" ) bit -- ;
112128 const lo = bit + 1 ;
113129 const width = hi - lo + 1 ;
114- if ( width <= 0 ) { bit -- ; continue ; }
115130
116131 const bits = match . slice ( match . length - 1 - hi , match . length - lo ) . join ( "" ) ;
117132
@@ -188,23 +203,18 @@ function expandBitfieldFields(fields, totalBits = 32) {
188203
189204 // Expand multi-segment fields into individual entries
190205 for ( const field of fields ) {
206+ // Most fields are contiguous and don't carry a segments array; fall back to [from..to] so they still render.
191207 const segments = Array . isArray ( field . segments ) && field . segments . length
192208 ? field . segments
193209 : [ { from : field . from , to : field . to } ] ;
194210
195211 for ( const seg of segments ) {
196- const hi = seg . from ;
197- const lo = seg . to ;
198- const width = hi - lo + 1 ;
199- if ( width <= 0 ) continue ;
200- const label =
201- segments . length > 1
202- ? `${ field . label } `
203- : field . label ;
212+ const width = seg . from - seg . to + 1 ;
213+ const label = field . label ;
204214 expanded . push ( {
205215 label,
206- from : hi ,
207- to : lo ,
216+ from : seg . from ,
217+ to : seg . to ,
208218 width,
209219 kind : field . kind
210220 } ) ;
@@ -273,7 +283,7 @@ module.exports = function loadInstructions() {
273283 reg : filledFields . map ( f => {
274284 let name = f . label ;
275285 // Constant fields have their label part before "=" removed (e.g., "funct7=0000000" → "0000000").
276- if ( f . kind === "const" && typeof name === "string" ) {
286+ if ( f . kind === "const" ) {
277287 const idx = name . indexOf ( "=" ) ;
278288 if ( idx !== - 1 && idx + 1 < name . length ) {
279289 name = name . slice ( idx + 1 ) ;
0 commit comments