diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa03246..029d405 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,16 +120,18 @@ jobs: -- -t "TinyIris should generate valid System Verilog for TinyIris" # PD hardens the two UCIe blocks as one hierarchical block, so both links - # have to sit inside a single UcieComplex module and share one UcieTL. - # Giving a port its own UcieTLParams.moduleId splits them again, and - # nothing else in the flow would notice. - - name: Check the UCIe blocks are grouped and deduped + # have to sit inside a single UcieComplex module. Each link is still its + # own UcieTL -- UcieTLParams.moduleId 0 and 1 -- so that PD can give each + # the lane clock distribution network laid out for its own bump field. + # Dropping a port's moduleId folds them back into one module, and nothing + # else in the flow would notice. + - name: Check the UCIe blocks are grouped, with one UcieTL per link run: | out=build/Iris_should_generate_valid_System_Verilog - n=$(grep -lE '^module UcieTL(_[A-Za-z0-9]+)?\(' "$out"/*.sv | wc -l) - if [ "$n" -ne 1 ]; then - echo "::error::expected 1 UcieTL module in $out, found $n" - grep -hE '^module UcieTL(_[A-Za-z0-9]+)?\(' "$out"/*.sv || true + tls=$(grep -hoE '^module UcieTL(_[A-Za-z0-9]+)?\(' "$out"/*.sv \ + | sed -E 's/^module //; s/\($//' | sort | paste -sd' ' -) + if [ "$tls" != "UcieTL UcieTL_1" ]; then + echo "::error::expected UcieTL modules UcieTL and UcieTL_1 in $out, found: ${tls:-none}" exit 1 fi if [ ! -f "$out/UcieComplex.sv" ]; then diff --git a/src/GenUcieHeader.scala b/src/GenUcieHeader.scala index cba06f1..12c67d8 100644 --- a/src/GenUcieHeader.scala +++ b/src/GenUcieHeader.scala @@ -29,19 +29,21 @@ object GenUcieHeader { def render(): String = { val ports = uciePorts // The header holds one register layout, so every port has to agree on - // everything that layout depends on. Address and moduleId do not reach it: - // the offsets are relative to each port's own MMIO base, and the moduleId - // only names modules. + // everything that layout depends on. Address, moduleId and the clock + // distribution netlist do not reach it: the offsets are relative to each + // port's own MMIO base, the moduleId only names modules, and the netlist is + // physical design. val canonical = ports.map( _.copy( address = ports.head.address, - moduleId = ports.head.moduleId + moduleId = ports.head.moduleId, + clkDistLayout = ports.head.clkDistLayout ) ) require( canonical.forall(_ == canonical.head), "UCIe ports must share a register layout, but their parameters differ " + - s"beyond address and moduleId: ${canonical.distinct.mkString("\n")}" + s"beyond address, moduleId and clock distribution: ${canonical.distinct.mkString("\n")}" ) edu.berkeley.cs.uciedigital.tilelink.GenUcieHeader .render(ports.head, regenCommand) diff --git a/src/Iris.scala b/src/Iris.scala index 85abb95..e6b9c34 100644 --- a/src/Iris.scala +++ b/src/Iris.scala @@ -503,10 +503,11 @@ class IrisConfig(sim: Boolean = false) routerParams = testchipip.soc.OffchipRouterParams(tableEntries = 4), ports = Seq( // PD hardens the two UCIe links together, so UcieComplexPort puts - // both instances inside one UcieComplex module. They are identical - // logic and share a `moduleId`, so they dedup into a single UcieTL - // underneath it; splitting them again is a matter of giving one its - // own `moduleId`. + // both instances inside one UcieComplex module. Each link is still + // its own module, `moduleId` 0 and 1, so that a physical design can + // give each the lane clock distribution network laid out for its + // own bump field (see `ClkDistLayout`). Each link also has its own + // high-speed and digital bypass clock bumps. UcieComplexPort(edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( address = 0x200000, managerWhere = SBUS, @@ -517,7 +518,8 @@ class IrisConfig(sim: Boolean = false) address = 0x208000, managerWhere = SBUS, numLanes = 16, - includeDefaultModels = true + includeDefaultModels = true, + moduleId = 1 )) ))) ++ new WithIrisUncore(sim)