Files
retroSoC/publications/datasheets/diagram-packages.typ
T
maksyuki 8d735aafbd feat: refactor tests and update configurations for OpenC906 migration
- Adjusted slot assignments in  to reflect new peripheral mappings.
- Updated  to validate new OpenC906 configurations, including generator and profile checks.
- Modified  to reference the new Std-series VexiiRiscv generator.
- Changed cache maintenance instructions in  to align with C906 specifications.
- Revised assertions in  to account for internal unit removals.
- Updated  to reflect changes in peripheral interface counts and policies.
- Added new test file  to ensure preservation of the frozen HP configuration in the Std-series generator.
2026-10-10 15:15:21 +08:00

379 lines
20 KiB
Typst
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#import "style.typ": data, ink, gold, pale-gold, gray, muted, rhythm, code, ds-table, source-note, change-start, change-end
#import "@preview/bytefield:0.0.8" as bf
#import "@preview/rivet:0.3.1" as rv
#import "@preview/blockcell:0.1.0" as bc
#import "@preview/circuiteria:0.2.1" as ce
#import "@preview/cetz:0.3.4" as c3
#let diagrams = data.system_reference.illustrations
#let diagram-position(kind, package, id) = context {
let position=here().position()
metadata((kind:kind,package:package,id:id,page:position.page,x:position.x.pt(),y:position.y.pt()))
}
#let diagram-use(package, id) = diagram-position("publication-diagram",package,id)
#let diagram-end(package, id) = diagram-position("publication-diagram-end",package,id)
#let blue-gray = rgb("#E7EFF6")
#let diagram-text(body) = text(font:"Inter",size:9pt,fill:ink,body)
#let diagram-note(body) = block(above:4pt,below:0pt)[
#set par(leading:rhythm.small-leading,spacing:rhythm.small-spacing)
#text(size:9pt,fill:muted,body)
]
#let dma-tcd-diagram() = block(width:100%,breakable:false)[
#diagram-use("bytefield","dma-tcd")
#set text(font:"Inter",size:9pt,fill:ink)
#show: bf.bf-config.with(row-height:24pt,field-font-size:9pt,note-font-size:9pt,
header-font-size:9pt,stroke:0.6pt+ink)
#let fields = ()
#for field in diagrams.dma_tcd {
if calc.rem(field.offset,16)==0 {
fields.push(bf.note(left)[#("0x"+upper(str(field.offset,base:16)))])
}
fields.push(bf.bytes(int(field.bits/8),fill:if field.role=="reserved" {gray}
else if field.role=="writeback" {blue-gray} else {pale-gold})[#field.name])
}
#bf.bytefield(bpr:128,msb:right,pre:(36pt,),post:(),
bf.bitheader(0,[0],32,[4],64,[8],96,[12],numbers:none,angle:0deg,text-size:9pt),..fields)
#diagram-note[Byte offsets increase left to right; four 16-byte rows form the 64-byte TCD.
Gold: software configuration. Blue-gray: current HAL result writeback. Gray: reserved.
Word serialization and 64-byte descriptor alignment are separate from the transfer width.]
#diagram-end("bytefield","dma-tcd")
]
#let sdio-command-diagram() = block(width:100%,breakable:false)[
#diagram-use("bytefield","sd-command")
#set text(font:"Inter",size:9pt,fill:ink)
#show: bf.bf-config.with(row-height:26pt,field-font-size:9pt,note-font-size:9pt,
header-font-size:9pt,stroke:0.6pt+ink)
#bf.bytefield(bpr:48,msb:left,pre:(),post:(),
bf.bitheader(47,40,8,0,angle:0deg,text-size:9pt),
..diagrams.sdio_command.map(f=>bf.bits(f.bits,fill:if f.bits==1 {gray} else {pale-gold})[#f.label]))
#diagram-note[Native SD command: S = start 0; T = host transmission 1; CMD = 6-bit command index;
ARG = 32-bit argument; CRC7 covers S through ARG; E = end 1. Bit 47 is transmitted first.]
#diagram-end("bytefield","sd-command")
]
#let instruction-row(example:none, start:0) = {
let ranges=(:)
let colors=(:)
let short=("CLASS":"CLS","OPCODE":"OP","PREDICATE":"PRED","SRC0":"S0","SRC1":"S1","IMMEDIATE":"IMM")
for field in diagrams.apu.fields.filter(f=>f.lsb >= start and f.lsb < start + 32) {
let span=str(field.lsb+field.bits - 1)+"-"+str(field.lsb)
let name=short.at(field.label,default:field.label)
if example!=none {
let value=example.values.at(field.name)
name=if field.name=="immediate" {"0x"+upper(str(value,base:16))} else {str(value)}
}
ranges.insert(span,(name:name))
colors.insert(span,if field.name in ("instruction_class","opcode","predicate") {pale-gold} else {gray})
}
let schema=rv.schema.load((structures:(main:(bits:32,start:start,ranges:ranges)),colors:(main:colors)))
rv.schema.render(schema,width:100%,config:rv.config.config(
default-font-family:"Inter",default-font-size:20pt,italic-font-family:"Inter",italic-font-size:20pt,
text-color:ink,link-color:ink,bit-i-color:ink,border-color:ink,background:white,
bit-width:32,bit-height:42,margins:(4,4,4,4),all-bit-i:false,full-page:false))
}
#let apu-instruction-diagram() = block(width:100%,breakable:false)[
#diagram-use("rivet","apu-common")
#set text(font:"Inter",size:9pt,fill:ink)
#diagram-text[*APUMC 64-bit instruction format*]
#instruction-row(start:32)
#instruction-row(start:0)
#for example in diagrams.apu.examples {
block(above:6pt,below:2pt)[#diagram-text[#example.title · #example.word]]
instruction-row(example:example,start:32)
instruction-row(example:example,start:0)
}
#diagram-note[CLS = instruction class; OP = opcode; PRED = predicate; DST/S0/S1 = register indexes;
AUX = operation-specific selector; IMM = immediate. These are internal APU instructions,
not LP/HP RISC-V extensions. Examples demonstrate encoding, not completed codec execution.]
#diagram-end("rivet","apu-common")
]
#let memory-window-diagram() = block(width:100%,breakable:false)[
#diagram-use("blockcell","product-memory-windows")
#set text(font:"Inter",size:9pt,fill:ink)
#set par(leading:rhythm.small-leading,spacing:5pt)
#for window in diagrams.windows {
block(above:0pt,below:4pt,grid(columns:(32mm,87mm,47mm),gutter:2mm,
bc.cell(width:100%,height:12mm,fill:pale-gold,stroke:0.6pt+ink)[#align(center+horizon)[#window.symbol]],
bc.cell(width:100%,height:12mm,fill:gray,stroke:0.6pt+ink)[#align(center+horizon)[#window.base_hex … #window.end_hex]],
bc.cell(width:100%,height:12mm,fill:white,stroke:0.6pt+ink)[#align(center+horizon)[#window.size_label]]))
}
#diagram-note[Selected PRODUCT memory address windows, not to scale. The reference SRAM is 32 KiB.
FLASH is a boot alias; serial-memory apertures do not establish fitted device capacity.
Register windows and reserved gaps remain in the complete address tables.]
#diagram-end("blockcell","product-memory-windows")
]
#let cache-boundary-diagram() = block(width:100%,breakable:false)[
#diagram-use("blockcell","cache-boundaries")
#set text(font:"Inter",size:9pt,fill:ink)
#let c=diagrams.cache
#diagram-text[Relative byte boundaries: 0 · #c.granule · #c.covered_bytes]
#v(4pt)
#bc.region(width:100%,fill:white,stroke:0.6pt+ink,radius:0pt)[
#bc.cell(width:77mm,height:12mm,fill:gray,stroke:0.6pt+ink)[#align(center+horizon)[Cache line 0: bytes 0–63]]
#h(2mm)
#bc.cell(width:77mm,height:12mm,fill:gray,stroke:0.6pt+ink)[#align(center+horizon)[Cache line 1: bytes 64–127]]
#linebreak()
#v(4pt)
#bc.cell(width:19mm,height:15mm,fill:gray,stroke:0.6pt+ink)[16 B \ before]
#bc.cell(width:57mm,height:15mm,fill:pale-gold,stroke:0.6pt+ink)[48 B payload \ bytes 16–63]
#h(2mm)
#bc.cell(width:19mm,height:15mm,fill:pale-gold,stroke:0.6pt+ink)[16 B \ payload]
#bc.cell(width:57mm,height:15mm,fill:gray,stroke:0.6pt+ink)[48 B after \ bytes 80–127]
]
#diagram-note[A 64-byte payload at offset 16 touches two 64-byte HP D-cache lines, so
maintenance spans both. Keep boundary bytes under compatible ownership. Relative offsets are illustrative;
this is not a verified HP cache tag/set/way layout or an allocated buffer address.]
#diagram-end("blockcell","cache-boundaries")
]
#let uart-fifo-diagram() = block(width:100%,breakable:false)[
#diagram-use("blockcell","uart-fifos")
#set text(font:"Inter",size:9pt,fill:ink)
#for (name,geometry) in diagrams.uart_fifo.pairs() {
block(above:4pt,below:6pt)[
*#upper(name) FIFO: #geometry.depth entries × #geometry.bits bits*
#linebreak()
#v(3pt)
#bc.region(width:100%,fill:white,stroke:0.6pt+ink,radius:0pt)[
#for index in ("0","1","2","…","63") {
bc.cell(width:29mm,height:13mm,fill:gray,stroke:0.6pt+ink)[#align(center+horizon)[Entry #index]]
h(2pt)
}
]
]
}
#bc.cell(width:103mm,height:14mm,fill:pale-gold,stroke:0.6pt+ink)[#align(center+horizon)[RX entry: data bits 7:0]]
#h(2mm)
#bc.cell(width:61mm,height:14mm,fill:blue-gray,stroke:0.6pt+ink)[#align(center+horizon)[Error metadata bits 11:8]]
#diagram-note[Storage slots are shown schematically; the drawing is not a live occupancy snapshot.
TX stores eight data bits. RX stores eight data bits plus four error bits; the 32-bit APB RXDATA
access width is a separate register interface. Pointer, flush and watermark rules remain in the UART contract.]
#diagram-end("blockcell","uart-fifos")
]
#let circuit-diagram(id) = block(width:100%,breakable:false)[
#diagram-use("circuiteria",id)
#set text(font:"Inter",size:9pt,fill:ink)
#let circuit=diagrams.circuits.at(id)
#ce.circuit(length:1cm,{
for node in circuit.nodes {
let ports=(:)
let margins=(:)
for side in ("west","east","north","south") {
ports.insert(side,node.ports.filter(p=>p.side==side).map(p=>(id:p.id,name:"")))
}
for (side,values) in node.at("port_margins",default:(:)) {
margins.insert(side,values.map(v=>v*1%))
}
ce.element.block(id:node.id,x:node.x,y:node.y,w:node.w,h:node.h,
name:block(width:node.w*1cm - 8pt)[
#set par(leading:2pt,spacing:0pt)
#align(center)[#node.title.split("\n").map(diagram-text).join(linebreak())]
],
ports:ports,ports-margins:margins,
fill:if node.id in ("reg","loader") {pale-gold} else {gray},
radius:0.06,stroke:0.6pt+ink)
}
for (index,edge) in circuit.edges.enumerate() {
let src=edge.from.split(".")
let dst=edge.to.split(".")
ce.wire.wire("edge"+str(index),(src.first()+"-port-"+src.last(),dst.first()+"-port-"+dst.last()),
bus:edge.kind=="data",name:none,color:if edge.at("availability",default:"")=="admission-blocked" {muted} else {ink},dashed:edge.kind=="control",directed:true,
style:edge.style,rotate-name:false,zigzag-dir:edge.at("zigzag-dir",default:"vertical"),
guided-sides:edge.at("guided-sides",default:("east","west")),
dodge-y:edge.at("dodge-y",default:0),
dodge-margins:edge.at("dodge-margins",default:(5,5)).map(v=>v*1%))
}
// Keep label backplates above every route, including later crossing wires.
for (index,edge) in circuit.edges.enumerate() {
let anchors=if edge.style=="dodge" {("dodge-start","dodge-end")} else {("start","end")}
c3.draw.content(("edge"+str(index)+"."+anchors.first(),50%,"edge"+str(index)+"."+anchors.last()),
box(fill:white,inset:(x:2pt,y:1pt),diagram-text(edge.label)))
}
})
#diagram-note[Solid arrows: data paths. Dashed arrows: control signals. #circuit.note]
#diagram-end("circuiteria",id)
]
#let binary-diagram(record) = block(width:100%,breakable:false)[
#diagram-use("bytefield",record.id)
#set text(font:"Inter",size:9pt,fill:ink)
#show: bf.bf-config.with(row-height:26pt,field-font-size:9pt,note-font-size:9pt,
header-font-size:9pt,stroke:0.6pt+ink)
#let field-fill(field) = {
if field.at("role",default:"")=="reserved" {gray}
else if field.at("role",default:"") in ("writeback","mixed") {blue-gray} else {pale-gold}
}
#if record.kind=="bytes" {
let bytes=int(record.bits/8)
let row-bytes=if calc.rem(bytes,16)==0 {16} else {12}
let header=()
for n in range(0,row-bytes,step:4) {header.push(n*8); header.push([#n])}
let cells=()
for field in record.fields {
if calc.rem(field.offset,row-bytes)==0 {
cells.push(bf.note(left)[#("0x"+upper(str(field.offset,base:16)))])
}
cells.push(bf.bytes(int(field.bits/8),fill:field-fill(field))[#field.label])
}
bf.bytefield(bpr:row-bytes*8,msb:right,pre:(36pt,),post:(),
bf.bitheader(..header,numbers:none,angle:0deg,text-size:9pt),..cells)
} else {
for row in record.at("rows",default:(record,)) {
if "label" in row {block(above:4pt,below:4pt)[#text(weight:"semibold",row.label)]}
let compressed=row.at("compressed",default:record.at("compressed",default:false))
let groups=row.at("display_groups",default:record.at("display_groups",default:none))
let fields=if groups==none {row.fields} else {
groups.map(g=>if g.first()==g.last() {row.fields.at(g.first())} else {
(label:"Words "+str(g.first())+" … "+str(g.last()),bits:32,role:"configuration")})
}
let cells=fields.map(f=>bf.bits(if compressed {32} else {f.bits},fill:field-fill(f))[#f.label])
let bits=if compressed {fields.len()*32} else {row.bits}
let msb=record.order=="msb"
bf.bytefield(bpr:bits,msb:if msb {left} else {right},pre:(),post:(),
..if compressed {()} else {(bf.bitheader(if msb {bits - 1} else {0},if msb {0} else {bits - 1},angle:0deg,text-size:9pt),)},
..cells)
}
}
#diagram-note[#record.note]
#diagram-end("bytefield",record.id)
]
#let binary-figures(chapter, section) = {
for (id,record) in diagrams.layouts.pairs().filter(((id,r))=>chapter in r.chapters and r.section==section) {
if record.chapters.first()==chapter and not "existing_label" in record {
block(breakable:false)[
#change-start("binary-"+id,record.title,category:"added")
#figure(binary-diagram(record),kind:image,supplement:[Figure],caption:[#record.title.])
#label("binary-"+id)
#for path in record.sources {source-note(path,title:"Format source: "+path.split("/").last())}
#change-end("binary-"+id)
]
} else if record.chapters.first()!=chapter {
block[
#change-start("binary-link-"+chapter+"-"+id,record.title+" shared-instance reference",category:"cross-reference")
Shared format: #link(label(record.at("existing_label",default:"binary-"+id)))[#record.title].
#record.at("shared_notes",default:(:)).at(chapter,default:"Apply this instance's address, pads and interrupt routing.")
#change-end("binary-link-"+chapter+"-"+id)
]
}
}
}
#let storage-diagram(record) = block(width:100%,breakable:false)[
#diagram-use("blockcell",record.id)
#set text(font:"Inter",size:9pt,fill:ink)
#for store in record.stores {
block(above:4pt,below:7pt)[
#text(weight:"semibold")[#store.title: #store.depth × #if store.bits > 512 {
[#int(store.bits/8) bytes]
} else {[#store.bits bits]}]
#v(3pt)
#let slots=if store.depth <= 4 {range(store.depth).map(str)} else {("0","1","2","…",str(store.depth - 1))}
#bc.region(width:100%,fill:white,stroke:0.6pt+ink,radius:0pt)[
#grid(columns:(1fr,)*slots.len(),gutter:2mm,
..slots.map(slot=>bc.cell(width:100%,height:11mm,fill:gray,stroke:0.6pt+ink)[#align(center+horizon)[#slot]]))
]
]
}
#for row in record.at("ranges",default:()) {
block(above:0pt,below:4pt,grid(columns:(54mm,85mm,27mm),gutter:2mm,
bc.cell(width:100%,height:12mm,fill:pale-gold,stroke:0.6pt+ink)[#align(center+horizon)[#row.name]],
bc.cell(width:100%,height:12mm,fill:gray,stroke:0.6pt+ink)[#align(center+horizon)[#row.base_hex … #row.end_hex]],
bc.cell(width:100%,height:12mm,fill:white,stroke:0.6pt+ink)[#align(center+horizon)[#row.size_label]]))
}
#if "sections" in record {
block(above:3pt,below:5pt)[#code(record.profile)]
grid(columns:(1fr,)*record.sections.len(),gutter:2mm,
..record.sections.map(s=>bc.cell(width:100%,height:24mm,fill:pale-gold,stroke:0.6pt+ink)[
#align(center+horizon)[*#s.name#if s.name!=".init" and s.includes_init {text(" + .init")}* \ VMA: #s.vma \ LMA: #if s.name==".bss" {[No load bytes]} else {s.lma}]
]))
block(above:5pt)[Stack top: end of #record.stack_region. Startup: #code(record.startup).]
}
#for budget in record.at("budgets",default:()) {
block(above:5pt,below:5pt)[
#text(weight:"semibold",budget.name)
#v(3pt)
#grid(columns:(1fr,1fr,1fr,1fr),gutter:2mm,
..(("Payload",budget.payload_bytes),("Stored",budget.storage_bytes),("Stride",budget.stride_bytes),("Total",budget.total_bytes)).map(((name,value))=>
bc.cell(width:100%,height:13mm,fill:if name=="Total" {pale-gold} else {gray},stroke:0.6pt+ink)[#align(center+horizon)[#name \ #value B]]))
]
}
#diagram-note[#record.note]
#diagram-end("blockcell",record.id)
]
#let storage-figures(chapter, section:"functional") = {
for (id,record) in diagrams.storage.pairs().filter(((id,r))=>chapter in r.chapters and r.section==section) {
if record.chapters.first()==chapter and not "existing_label" in record {
block(breakable:false)[
#change-start("storage-"+id,record.title,category:"added")
#figure(storage-diagram(record),kind:image,supplement:[Figure],caption:[#record.title.])
#label("storage-"+id)
#for path in record.sources {source-note(path,title:"Storage source: "+path.split("/").last())}
#change-end("storage-"+id)
]
} else if record.chapters.first()!=chapter {
block[
#change-start("storage-link-"+chapter+"-"+id,record.title+" shared-instance reference",category:"cross-reference")
Shared storage geometry: #link(label(record.at("existing_label",default:"storage-"+id)))[#record.title].
Instance routing, ownership and enabled paths remain those described here.
#change-end("storage-link-"+chapter+"-"+id)
]
}
}
}
#let family-instruction-diagram(family) = block(width:100%,breakable:false)[
#diagram-use("rivet","apu-family-"+lower(family.name))
#set text(font:"Inter",size:9pt,fill:ink)
#diagram-text[*#family.name: class #family.class*]
#for start in (32,0) {
let ranges=(:)
let colors=(:)
for field in diagrams.apu.fields.filter(f=>f.lsb >= start and f.lsb < start + 32) {
let aliases=("instruction_class":"CLS","opcode":"OP","predicate":"PRED","dst":"DST","src0":"S0","src1":"S1","aux":"AUX","immediate":"IMM")
let name=if field.name=="instruction_class" {str(family.class)}
else if field.name=="opcode" or field.name in family.active {aliases.at(field.name)} else {"0"}
let span=str(field.lsb+field.bits - 1)+"-"+str(field.lsb)
ranges.insert(span,(name:name))
colors.insert(span,if field.name in ("instruction_class","opcode","predicate") {pale-gold} else {gray})
}
let schema=rv.schema.load((structures:(main:(bits:32,start:start,ranges:ranges)),colors:(main:colors)))
rv.schema.render(schema,width:100%,config:rv.config.config(
default-font-family:"Inter",default-font-size:20pt,italic-font-family:"Inter",italic-font-size:20pt,
text-color:ink,link-color:ink,bit-i-color:ink,border-color:ink,background:white,
bit-width:32,bit-height:42,margins:(4,4,4,4),all-bit-i:false,full-page:false))
}
#diagram-note[#family.note The diagram shows the union of admitted operand slots in this family;
individual opcodes reserve the slots listed below. Example #family.example.name: #family.example.word.]
#diagram-end("rivet","apu-family-"+lower(family.name))
]
#let apu-family-figures() = {
change-start("apu-family-guide","APU instruction-family coverage and target qualifications",category:"added")
[The following seven classes cover all currently defined operation codes. Variable slots are operands
admitted by the existing validator; zero slots are whole fields required to be zero.
Tool targets describe encoding acceptance, not enabled production codecs or successful payload execution.
Primitive masks shown are APUMC requirements for the encoded example, distinct from the APB capability word.
WAIT is source-dependent: P4 admits kernel/input/output-FIFO waits;
P5 also admits DMA, TX-stream and ring-writeback waits. Value ranges, mode bits, scratch bounds and entry control flow still apply.]
change-end("apu-family-guide")
for family in diagrams.apu.families {
change-start("apu-family-"+lower(family.name),"APU "+family.name+" instruction family",category:"added")
figure(family-instruction-diagram(family),kind:image,supplement:[Figure],caption:[APU #family.name instruction-family operand fields.])
ds-table("apu-opcodes-"+lower(family.name),[APU #family.name opcode and operand constraints],
([OP],[Operation],[Variable slots],[Zero slots],[Example mask],[Tool target]),
family.operations.map(op=>(code(op.opcode_hex),code(op.name),op.slots,op.zero_slots,code(op.required_mask),op.targets)),
widths:(0.3fr,1.1fr,1fr,1fr,0.85fr,0.65fr))
source-note("scripts/apu_isa.py",title:"Complete operand/value and target checks")
source-note("rtl/ip/multimedia/apu_microcode_pkg.sv",title:"RTL encoding and predicate checks")
change-end("apu-family-"+lower(family.name))
}
}