From 93447eb27468ee0bfb20d5e5bf0225bc04f35d30 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 24 May 2016 14:06:03 -0700 Subject: [PATCH 01/12] ahb: make hasti parameters location sensitive --- junctions/src/main/scala/hasti.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index bf25138a..28bd3011 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -32,11 +32,12 @@ trait HastiConstants } case class HastiParameters(dataBits: Int, addrBits: Int) -case object HastiKey extends Field[HastiParameters] +case object HastiId extends Field[String] +case class HastiKey(id: String) extends Field[HastiParameters] trait HasHastiParameters { implicit val p: Parameters - val hastiParams = p(HastiKey) + val hastiParams = p(HastiKey(p(HastiId))) val hastiAddrBits = hastiParams.addrBits val hastiDataBits = hastiParams.dataBits } From 7896c4157e859cb23e886fb66e45c0e795ac4cb8 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 16:10:57 -0700 Subject: [PATCH 02/12] ahb: parameterize poci --- junctions/src/main/scala/package.scala | 2 +- junctions/src/main/scala/poci.scala | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/junctions/src/main/scala/package.scala b/junctions/src/main/scala/package.scala index 3181064e..317b7109 100644 --- a/junctions/src/main/scala/package.scala +++ b/junctions/src/main/scala/package.scala @@ -1 +1 @@ -package object junctions extends HastiConstants with PociConstants +package object junctions extends HastiConstants diff --git a/junctions/src/main/scala/poci.scala b/junctions/src/main/scala/poci.scala index 01993fee..976fd304 100644 --- a/junctions/src/main/scala/poci.scala +++ b/junctions/src/main/scala/poci.scala @@ -3,20 +3,14 @@ package junctions import Chisel._ import cde.{Parameters, Field} -abstract trait PociConstants +class PociIO(implicit p: Parameters) extends HastiBundle()(p) { - val SZ_PADDR = 32 - val SZ_PDATA = 32 -} - -class PociIO extends Bundle -{ - val paddr = UInt(OUTPUT, SZ_PADDR) + val paddr = UInt(OUTPUT, hastiAddrBits) val pwrite = Bool(OUTPUT) val psel = Bool(OUTPUT) val penable = Bool(OUTPUT) - val pwdata = UInt(OUTPUT, SZ_PDATA) - val prdata = UInt(INPUT, SZ_PDATA) + val pwdata = UInt(OUTPUT, hastiDataBits) + val prdata = UInt(INPUT, hastiDataBits) val pready = Bool(INPUT) val pslverr = Bool(INPUT) } @@ -45,7 +39,7 @@ class HastiToPociBridge(implicit p: Parameters) extends HastiModule()(p) { } } - val haddr_reg = Reg(UInt(width = SZ_PADDR)) + val haddr_reg = Reg(UInt(width = hastiAddrBits)) val hwrite_reg = Reg(UInt(width = 1)) when (transfer) { haddr_reg := io.in.haddr @@ -62,7 +56,7 @@ class HastiToPociBridge(implicit p: Parameters) extends HastiModule()(p) { io.in.hresp := io.out.pslverr } -class PociBus(amap: Seq[UInt=>Bool]) extends Module +class PociBus(amap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModule()(p) { val io = new Bundle { val master = new PociIO().flip From 2f8a77f27aa05938007cd14e4c71ce9b45588f0b Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 15:57:10 -0700 Subject: [PATCH 03/12] ahb: include all AHB-lite constants --- junctions/src/main/scala/hasti.scala | 35 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 28bd3011..628857d0 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -5,28 +5,39 @@ import cde.{Parameters, Field} trait HastiConstants { + // Values for htrans val SZ_HTRANS = 2 - val HTRANS_IDLE = UInt(0, SZ_HTRANS) - val HTRANS_BUSY = UInt(1, SZ_HTRANS) - val HTRANS_NONSEQ = UInt(2, SZ_HTRANS) - val HTRANS_SEQ = UInt(3, SZ_HTRANS) + val HTRANS_IDLE = UInt(0, SZ_HTRANS) // No transfer requested, not in a burst + val HTRANS_BUSY = UInt(1, SZ_HTRANS) // No transfer requested, in a burst + val HTRANS_NONSEQ = UInt(2, SZ_HTRANS) // First (potentially only) request in a burst + val HTRANS_SEQ = UInt(3, SZ_HTRANS) // Following requests in a burst + // Values for hburst val SZ_HBURST = 3 - val HBURST_SINGLE = UInt(0, SZ_HBURST) - val HBURST_INCR = UInt(1, SZ_HBURST) - val HBURST_WRAP4 = UInt(2, SZ_HBURST) - val HBURST_INCR4 = UInt(3, SZ_HBURST) - val HBURST_WRAP8 = UInt(4, SZ_HBURST) - val HBURST_INCR8 = UInt(5, SZ_HBURST) - val HBURST_WRAP16 = UInt(6, SZ_HBURST) - val HBURST_INCR16 = UInt(7, SZ_HBURST) + val HBURST_SINGLE = UInt(0, SZ_HBURST) // Single access (no burst) + val HBURST_INCR = UInt(1, SZ_HBURST) // Incrementing burst of arbitrary length, not crossing 1KB + val HBURST_WRAP4 = UInt(2, SZ_HBURST) // 4-beat wrapping burst + val HBURST_INCR4 = UInt(3, SZ_HBURST) // 4-beat incrementing burst + val HBURST_WRAP8 = UInt(4, SZ_HBURST) // 8-beat wrapping burst + val HBURST_INCR8 = UInt(5, SZ_HBURST) // 8-beat incrementing burst + val HBURST_WRAP16 = UInt(6, SZ_HBURST) // 16-beat wrapping burst + val HBURST_INCR16 = UInt(7, SZ_HBURST) // 16-beat incrementing burst + // Values for hresp val SZ_HRESP = 1 val HRESP_OKAY = UInt(0, SZ_HRESP) val HRESP_ERROR = UInt(1, SZ_HRESP) + // Values for hsize are identical to TileLink MT_SZ + // ie: 8*2^SZ_HSIZE bit transfers val SZ_HSIZE = 3 + + // Values for hprot (a bitmask) val SZ_HPROT = 4 + def HPROT_DATA = UInt("b0001") // Data access or Opcode fetch + def HPROT_PRIVILEGED = UInt("b0010") // Privileged or User access + def HPROT_BUFFERABLE = UInt("b0100") // Bufferable or non-bufferable + def HPROT_CACHEABLE = UInt("b1000") // Cacheable or non-cacheable def dgate(valid: Bool, b: UInt) = Fill(b.getWidth, valid) & b } From ef2aae26a83d3555bb1249cf0fc883452073e71d Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 15:59:54 -0700 Subject: [PATCH 04/12] ahb: rename hreadyout to standard hready, mark hreadyin for death --- junctions/src/main/scala/hasti.scala | 24 ++++++++++++------------ junctions/src/main/scala/poci.scala | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 628857d0..9275428c 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -86,10 +86,10 @@ class HastiSlaveIO(implicit p: Parameters) extends HastiBundle()(p) { val hwdata = Bits(INPUT, hastiDataBits) val hrdata = Bits(OUTPUT, hastiDataBits) - val hsel = Bool(INPUT) - val hreadyin = Bool(INPUT) - val hreadyout = Bool(OUTPUT) - val hresp = UInt(OUTPUT, SZ_HRESP) + val hsel = Bool(INPUT) + val hreadyin = Bool(INPUT) // !!! non-standard signal + val hready = Bool(OUTPUT) + val hresp = UInt(OUTPUT, SZ_HRESP) } class HastiBus(amap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModule()(p) { @@ -137,11 +137,11 @@ class HastiBus(amap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModul } } val s1_hsels = Array.fill(amap.size){Reg(init = Bool(false))} - val hreadyouts = io.slaves.map(_.hreadyout) - val master_hready = s1_hsels.reduce(_||_) === Bool(false) || Mux1H(s1_hsels, hreadyouts) + val hreadys = io.slaves.map(_.hready) + val master_hready = s1_hsels.reduce(_||_) === Bool(false) || Mux1H(s1_hsels, hreadys) when (master_hready) { - val skid = s1_hsels.reduce(_||_) && (hsels zip hreadyouts).map{ case (s, r) => s && !r }.reduce(_||_) + val skid = s1_hsels.reduce(_||_) && (hsels zip hreadys).map{ case (s, r) => s && !r }.reduce(_||_) skb_valid := skid when (skid) { skb_haddr := io.master.haddr @@ -185,7 +185,7 @@ class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { val s1_grants = Array.fill(n){Reg(init = Bool(true))} (s1_grants zip grants) foreach { case (g1, g) => - when (io.out.hreadyout) { g1 := g } + when (io.out.hready) { g1 := g } } def sel[T <: Data](in: Seq[T], s1: Seq[T]) = @@ -201,7 +201,7 @@ class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { io.out.hsel := grants.reduce(_||_) (io.ins zipWithIndex) map { case (in, i) => { - when (io.out.hreadyout) { + when (io.out.hready) { when (grants(i)) { skb_valid(i) := Bool(false) } @@ -222,12 +222,12 @@ class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { } } io.out.hwdata := Mux1H(s1_grants, io.ins.map(_.hwdata)) - io.out.hreadyin := io.out.hreadyout + io.out.hreadyin := io.out.hready (io.ins zipWithIndex) foreach { case (in, i) => { val g1 = s1_grants(i) in.hrdata := dgate(g1, io.out.hrdata) - in.hreadyout := io.out.hreadyout && (!skb_valid(i) || g1) + in.hready := io.out.hready && (!skb_valid(i) || g1) in.hresp := dgate(g1, io.out.hresp) } } } @@ -264,7 +264,7 @@ class HastiSlaveToMaster(implicit p: Parameters) extends HastiModule()(p) { io.out.hmastlock := io.in.hmastlock io.out.hwdata := io.in.hwdata io.in.hrdata := io.out.hrdata - io.in.hreadyout := io.out.hready + io.in.hready := io.out.hready io.in.hresp := io.out.hresp } diff --git a/junctions/src/main/scala/poci.scala b/junctions/src/main/scala/poci.scala index 976fd304..b4248fe2 100644 --- a/junctions/src/main/scala/poci.scala +++ b/junctions/src/main/scala/poci.scala @@ -52,7 +52,7 @@ class HastiToPociBridge(implicit p: Parameters) extends HastiModule()(p) { io.out.penable := (state === s_access) io.out.pwdata := io.in.hwdata io.in.hrdata := io.out.prdata - io.in.hreadyout := ((state === s_access) & io.out.pready) | (state === s_idle) + io.in.hready := ((state === s_access) & io.out.pready) | (state === s_idle) io.in.hresp := io.out.pslverr } From 2b37f37335fbfdd986e977baeee3eb307c6307d5 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 16:23:32 -0700 Subject: [PATCH 05/12] ahb: helper methods --- junctions/src/main/scala/hasti.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 9275428c..50a5f822 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -72,6 +72,10 @@ class HastiMasterIO(implicit p: Parameters) extends HastiBundle()(p) { val hready = Bool(INPUT) val hresp = UInt(INPUT, SZ_HRESP) + + def isNSeq(dummy:Int=0) = htrans === HTRANS_NONSEQ // SEQ does not start a NEW request + def isHold(dummy:Int=0) = htrans === HTRANS_BUSY || htrans === HTRANS_SEQ + def isIdle(dummy:Int=0) = htrans === HTRANS_IDLE || htrans === HTRANS_BUSY } class HastiSlaveIO(implicit p: Parameters) extends HastiBundle()(p) { From 0368b6db6b61db57cb1a259a12d9611d5a8a8b0d Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 13:21:12 -0700 Subject: [PATCH 06/12] ahb: replace defective crossbar with a functional one The previous crossbar had the following bugs: 1. a bursting master could be preempted the AHB-lite spec requires a slave receive the entire burst 2. a waited master could be replaced the AHB-lite spec requires haddr/etc to remain unchanged 3. hmastlock did no ensure exclusive access atomic operations could be pre-empted --- junctions/src/main/scala/hasti.scala | 215 ++++++++++++++++++++++++--- 1 file changed, 198 insertions(+), 17 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 50a5f822..5b0fb64e 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -96,6 +96,204 @@ class HastiSlaveIO(implicit p: Parameters) extends HastiBundle()(p) { val hresp = UInt(OUTPUT, SZ_HRESP) } +/* A diverted master is told hready when his address phase goes nowhere. + * In this case, we buffer his address phase request and replay it later. + * NOTE: this must optimize to nothing when divert is constantly false. + */ +class MasterDiversion(implicit p: Parameters) extends HastiModule()(p) { + val io = new Bundle { + val in = (new HastiMasterIO).flip + val out = (new HastiMasterIO) + val divert = Bool(INPUT) + } + + val full = Reg(init = Bool(false)) + val buffer = Reg(new HastiMasterIO) + + when (io.out.hready) { + full := Bool(false) + } + when (io.divert) { + full := Bool(true) + buffer := io.in + } + + // If the master is diverted, he must also have been told hready + assert (!io.divert || io.in.hready); + + // Replay the request we diverted + io.out.htrans := Mux(full, buffer.htrans, io.in.htrans) + io.out.hmastlock := Mux(full, buffer.hmastlock, io.in.hmastlock) + io.out.haddr := Mux(full, buffer.haddr, io.in.haddr) + io.out.hwrite := Mux(full, buffer.hwrite, io.in.hwrite) + io.out.hburst := Mux(full, buffer.hburst, io.in.hburst) + io.out.hsize := Mux(full, buffer.hsize, io.in.hsize) + io.out.hprot := Mux(full, buffer.hprot, io.in.hprot) + io.out.hwdata := Mux(full, buffer.hwdata, io.in.hwdata) + + // Pass slave response back + io.in.hrdata := io.out.hrdata + io.in.hresp := io.out.hresp + io.in.hready := io.out.hready && !full // Block master while we steal his address phase +} + +/* Masters with lower index have priority over higher index masters. + * However, a lower priority master will retain control of a slave when EITHER: + * 1. a burst is in progress (switching slaves mid-burst violates AHB-lite at slave) + * 2. a transfer was waited (the standard forbids changing requests in this case) + * + * If a master raises hmastlock, it will be waited until no other master has inflight + * requests; then, it acquires exclusive control of the crossbar until hmastlock is low. + * + * To implement an AHB-lite crossbar, it is important to realize that requests and + * responses are coupled. Unlike modern bus protocols where the response data has flow + * control independent of the request data, in AHB-lite, both flow at the same time at + * the sole discretion of the slave via the hready signal. The address and data are + * delivered on two back-to-back cycles, the so-called address and data phases. + * + * Masters can only be connected to a single slave at a time. If a master had two different + * slave connections on the address and data phases, there would be two independent hready + * signals. An AHB-lite slave can assume that data flows when it asserts hready. If the data + * slave deasserts hready while the address slave asserts hready, the master is put in the + * impossible position of being in data phase on two slaves at once. For this reason, when + * a master issues back-to-back accesses to distinct slaves, we inject a pipeline bubble + * between the two requests to limit the master to just a single slave at a time. + * + * Conversely, a slave CAN have two masters attached to it. This is unproblematic, because + * the only signal which governs data flow is hready. Thus, both masters can be stalled + * safely by the single slave. + */ +class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModule()(p) { + val io = new Bundle { + val masters = Vec(nMasters, new HastiMasterIO).flip + val slaves = Vec(addressMap.size, new HastiSlaveIO).flip + } + + val nSlaves = addressMap.size + // !!! handle hmastlock + + // Setup diversions infront of each master + val diversions = Seq.tabulate(nMasters) { m => Module(new MasterDiversion) } + (io.masters zip diversions) foreach { case (m, d) => d.io.in <> m } + + // Handy short-hand + val masters = diversions map (_.io.out) + val slaves = io.slaves + + // This matrix governs the master-slave connections in the address phase + // It is indexed by addressPhaseGrantSM(slave)(master) + // It is guaranteed to have at most one 'true' per column and per row + val addressPhaseGrantSM = Wire(Vec(nSlaves, Vec(nMasters, Bool()))) + // This matrix governs the master-slave connections in the data phase + // It is guaranteed to have at most one 'true' per column and per row + val dataPhaseGrantSM = Reg (init = Vec.fill(nSlaves)(Vec.fill(nMasters)(Bool(false)))) + // This matrix is the union of the address and data phases. + // It is transposed with respect to the two previous matrices. + // It is guaranteed to contain at most one 'true' per master row. + // However, two 'true's per slave column are permitted. + val unionGrantMS = Vec.tabulate(nMasters) { m => Vec.tabulate(nSlaves) { s => + addressPhaseGrantSM(s)(m) || dataPhaseGrantSM(s)(m) } } + + // Confirm the guarantees made above + def justOnce(v: Vec[Bool]) = v.fold(Bool(false)) { case (p, v) => + assert (!p || !v) + p || v + } + addressPhaseGrantSM foreach { s => justOnce(s) } + unionGrantMS foreach { s => justOnce(s) } + + // Data phase follows address phase whenever the slave is ready + (slaves zip (dataPhaseGrantSM zip addressPhaseGrantSM)) foreach { case (s, (d, a)) => + when (s.hready) { d := a } + } + + // Record the grant state from the previous cycle; needed in case we hold access + val priorAddressPhaseGrantSM = RegNext(addressPhaseGrantSM) + + // If a master says BUSY or SEQ, it is in the middle of a burst. + // In this case, it MUST stay attached to the same slave as before. + // Otherwise, it would violate the AHB-lite specification as seen by + // the slave, which is guaranteed a complete burst of the promised length. + // One case where this matters is preventing preemption of low-prio masters. + // NOTE: this exposes a slave to bad addresses when a master is buggy + val holdBurstM = Vec(masters map { _.isHold() }) + + // Transform the burst hold requirement from master indexing to slave indexing + // We use the previous cycle's binding because the master continues the prior burst + val holdBurstS = Vec(priorAddressPhaseGrantSM map { m => Mux1H(m, holdBurstM) }) + + // If a slave says !hready to a request, it must retain the same master next cycle. + // The AHB-lite specification requires that a waited transfer remain unchanged. + // If we preempted a waited master, the new master's request could potentially differ. + val holdBusyS = RegNext(Vec(slaves map { s => !s.hready && s.hsel })) + + // Combine the above two grounds to determine if the slave retains its prior master + val holdS = Vec((holdBurstS zip holdBusyS) map ({ case (a,b) => a||b })) + + // Determine which master addresses match which slaves + val matchMS = Vec(masters map { m => Vec(addressMap map { afn => afn(m.haddr) }) }) + // Detect requests to nowhere; we need to allow progress in this case + val nowhereM = Vec(matchMS map { s => !s.reduce(_ || _) }) + + // Detect if we need to inject a pipeline bubble between the master requests. + // Divert masters already granted a data phase different from next request. + // NOTE: if only one slave, matchMS is always true => bubble always false + // => the diversion registers are optimized away as they are unread + // NOTE: bubble => dataPhase => have an hready signal + val bubbleM = + Vec.tabulate(nMasters) { m => + Vec.tabulate(nSlaves) { s => dataPhaseGrantSM(s)(m) && !matchMS(m)(s) } + .reduce(_ || _) } + + // Requested access to slaves from masters (pre-arbitration) + // NOTE: isNSeq does NOT include SEQ; thus, masters who are midburst do not + // request access to a new slave. They stay tied to the old and do not get two. + // NOTE: if a master was waited, it must repeat the same request as last cycle; + // thus, it will request the same slave and not end up with two (unless buggy). + val NSeq = Vec(masters.map(_.isNSeq())) + val requestSM = Vec.tabulate(nSlaves) { s => Vec.tabulate(nMasters) { m => matchMS(m)(s) && NSeq(m) && !bubbleM(m) } } + + // Select at most one master request per slave (lowest index = highest priority) + val selectedRequestSM = Vec(requestSM map { m => Vec(PriorityEncoderOH(m)) }) + + // Calculate new crossbar interconnect state + addressPhaseGrantSM := Vec((holdS zip (priorAddressPhaseGrantSM zip selectedRequestSM)) + map { case (h, (p, r)) => Mux(h, p, r) }) + + // If we diverted a master, we need to absorb his address phase to replay later + for (m <- 0 until nMasters) { + diversions(m).io.divert := bubbleM(m) && NSeq(m) && masters(m).hready + } + + // Master muxes (address and data phase are the same) + (masters zip (unionGrantMS zip nowhereM)) foreach { case (m, (g, n)) => { + // If the master is connected to a slave, the slave determines hready. + // However, if no slave is connected, for progress report ready anyway, if: + // bad address (swallow request) OR idle (permit stupid slaves to move FSM) + val autoready = n || m.isIdle() + m.hready := Mux1H(g, slaves.map(_.hready ^ autoready)) ^ autoready + m.hrdata := Mux1H(g, slaves.map(_.hrdata)) + m.hresp := Mux1H(g, slaves.map(_.hresp)) + } } + + // Slave address phase muxes + (slaves zip addressPhaseGrantSM) foreach { case (s, g) => { + s.htrans := Mux1H(g, masters.map(_.htrans)) // defaults to HTRANS_IDLE (0) + s.haddr := Mux1H(g, masters.map(_.haddr)) + s.hmastlock := Mux1H(g, masters.map(_.hmastlock)) // !!! use global crossbar lock state + s.hwrite := Mux1H(g, masters.map(_.hwrite)) + s.hsize := Mux1H(g, masters.map(_.hsize)) + s.hburst := Mux1H(g, masters.map(_.hburst)) + s.hprot := Mux1H(g, masters.map(_.hprot)) + s.hsel := g.reduce(_ || _) + } } + + // Slave data phase muxes + (slaves zip dataPhaseGrantSM) foreach { case (s, g) => { + s.hwdata := Mux1H(g, masters.map(_.hwdata)) + } } +} + class HastiBus(amap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModule()(p) { val io = new Bundle { val master = new HastiMasterIO().flip @@ -236,23 +434,6 @@ class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { } } } -class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool]) - (implicit p: Parameters) extends HastiModule()(p) { - val io = new Bundle { - val masters = Vec(nMasters, new HastiMasterIO).flip - val slaves = Vec(addressMap.size, new HastiSlaveIO).flip - } - - val buses = List.fill(nMasters){Module(new HastiBus(addressMap))} - val muxes = List.fill(addressMap.size){Module(new HastiSlaveMux(nMasters))} - - (buses.map(b => b.io.master) zip io.masters) foreach { case (b, m) => b <> m } - (muxes.map(m => m.io.out) zip io.slaves ) foreach { case (x, s) => x <> s } - for (m <- 0 until nMasters; s <- 0 until addressMap.size) yield { - buses(m).io.slaves(s) <> muxes(s).io.ins(m) - } -} - class HastiSlaveToMaster(implicit p: Parameters) extends HastiModule()(p) { val io = new Bundle { val in = new HastiSlaveIO From f30f8d9f79631d45f3ecb568d81cfc3fbac9ea6b Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 16:31:06 -0700 Subject: [PATCH 07/12] ahb: reduce obsolete degenerate cases of a crossbar --- junctions/src/main/scala/hasti.scala | 133 ++------------------------- 1 file changed, 8 insertions(+), 125 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 5b0fb64e..19726cf1 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -300,69 +300,9 @@ class HastiBus(amap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModul val slaves = Vec(amap.size, new HastiSlaveIO).flip } - // skid buffer - val skb_valid = Reg(init = Bool(false)) - val skb_haddr = Reg(UInt(width = hastiAddrBits)) - val skb_hwrite = Reg(Bool()) - val skb_hsize = Reg(UInt(width = SZ_HSIZE)) - val skb_hburst = Reg(UInt(width = SZ_HBURST)) - val skb_hprot = Reg(UInt(width = SZ_HPROT)) - val skb_htrans = Reg(UInt(width = SZ_HTRANS)) - val skb_hmastlock = Reg(Bool()) - val skb_hwdata = Reg(UInt(width = hastiDataBits)) - - val master_haddr = Mux(skb_valid, skb_haddr, io.master.haddr) - val master_hwrite = Mux(skb_valid, skb_hwrite, io.master.hwrite) - val master_hsize = Mux(skb_valid, skb_hsize, io.master.hsize) - val master_hburst = Mux(skb_valid, skb_hburst, io.master.hburst) - val master_hprot = Mux(skb_valid, skb_hprot, io.master.hprot) - val master_htrans = Mux(skb_valid, skb_htrans, io.master.htrans) - val master_hmastlock = Mux(skb_valid, skb_hmastlock, io.master.hmastlock) - val master_hwdata = Mux(skb_valid, skb_hwdata, io.master.hwdata) - - val hsels = PriorityEncoderOH( - (io.slaves zip amap) map { case (s, afn) => { - s.haddr := master_haddr - s.hwrite := master_hwrite - s.hsize := master_hsize - s.hburst := master_hburst - s.hprot := master_hprot - s.htrans := master_htrans - s.hmastlock := master_hmastlock - s.hwdata := master_hwdata - afn(master_haddr) && master_htrans.orR - }}) - - (io.slaves zip hsels) foreach { case (s, hsel) => { - s.hsel := hsel - s.hreadyin := skb_valid || io.master.hready - } } - - val s1_hsels = Array.fill(amap.size){Reg(init = Bool(false))} - val hreadys = io.slaves.map(_.hready) - val master_hready = s1_hsels.reduce(_||_) === Bool(false) || Mux1H(s1_hsels, hreadys) - - when (master_hready) { - val skid = s1_hsels.reduce(_||_) && (hsels zip hreadys).map{ case (s, r) => s && !r }.reduce(_||_) - skb_valid := skid - when (skid) { - skb_haddr := io.master.haddr - skb_hwrite := io.master.hwrite - skb_hsize := io.master.hsize - skb_hburst := io.master.hburst - skb_hprot := io.master.hprot - skb_htrans := io.master.htrans - skb_hmastlock := io.master.hmastlock - } - - (s1_hsels zip hsels) foreach { case (s1, s) => - s1 := s - } - } - - io.master.hready := !skb_valid && master_hready - io.master.hrdata := Mux1H(s1_hsels, io.slaves.map(_.hrdata)) - io.master.hresp := Mux1H(s1_hsels, io.slaves.map(_.hresp)) + val bar = Module(new HastiXbar(1, amap)) + io.master <> bar.io.masters(0) + io.slaves <> bar.io.slaves } class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { @@ -370,68 +310,11 @@ class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { val ins = Vec(n, new HastiSlaveIO) val out = new HastiSlaveIO().flip } - - // skid buffers - val skb_valid = Array.fill(n){Reg(init = Bool(false))} - val skb_haddr = Array.fill(n){Reg(UInt(width = hastiAddrBits))} - val skb_hwrite = Array.fill(n){Reg(Bool())} - val skb_hsize = Array.fill(n){Reg(UInt(width = SZ_HSIZE))} - val skb_hburst = Array.fill(n){Reg(UInt(width = SZ_HBURST))} - val skb_hprot = Array.fill(n){Reg(UInt(width = SZ_HPROT))} - val skb_htrans = Array.fill(n){Reg(UInt(width = SZ_HTRANS))} - val skb_hmastlock = Array.fill(n){Reg(Bool())} - - val requests = (io.ins zip skb_valid) map { case (in, v) => in.hsel && in.hreadyin || v } - val grants = PriorityEncoderOH(requests) - - val s1_grants = Array.fill(n){Reg(init = Bool(true))} - - (s1_grants zip grants) foreach { case (g1, g) => - when (io.out.hready) { g1 := g } - } - - def sel[T <: Data](in: Seq[T], s1: Seq[T]) = - Vec((skb_valid zip s1 zip in) map { case ((v, s), in) => Mux(v, s, in) }) - - io.out.haddr := Mux1H(grants, sel(io.ins.map(_.haddr), skb_haddr)) - io.out.hwrite := Mux1H(grants, sel(io.ins.map(_.hwrite), skb_hwrite)) - io.out.hsize := Mux1H(grants, sel(io.ins.map(_.hsize), skb_hsize)) - io.out.hburst := Mux1H(grants, sel(io.ins.map(_.hburst), skb_hburst)) - io.out.hprot := Mux1H(grants, sel(io.ins.map(_.hprot), skb_hprot)) - io.out.htrans := Mux1H(grants, sel(io.ins.map(_.htrans), skb_htrans)) - io.out.hmastlock := Mux1H(grants, sel(io.ins.map(_.hmastlock), skb_hmastlock)) - io.out.hsel := grants.reduce(_||_) - - (io.ins zipWithIndex) map { case (in, i) => { - when (io.out.hready) { - when (grants(i)) { - skb_valid(i) := Bool(false) - } - when (!grants(i) && !skb_valid(i)) { - val valid = in.hsel && in.hreadyin - skb_valid(i) := valid - when (valid) { // clock-gate - skb_haddr(i) := in.haddr - skb_hwrite(i) := in.hwrite - skb_hsize(i) := in.hsize - skb_hburst(i) := in.hburst - skb_hprot(i) := in.hprot - skb_htrans(i) := in.htrans - skb_hmastlock(i) := in.hmastlock - } - } - } - } } - - io.out.hwdata := Mux1H(s1_grants, io.ins.map(_.hwdata)) - io.out.hreadyin := io.out.hready - - (io.ins zipWithIndex) foreach { case (in, i) => { - val g1 = s1_grants(i) - in.hrdata := dgate(g1, io.out.hrdata) - in.hready := io.out.hready && (!skb_valid(i) || g1) - in.hresp := dgate(g1, io.out.hresp) - } } + + val amap = Seq({ (_:UInt) => Bool(true)}) + val bar = Module(new HastiXbar(n, amap)) + io.ins <> bar.io.masters + io.out <> bar.io.slaves(0) } class HastiSlaveToMaster(implicit p: Parameters) extends HastiModule()(p) { From 15cad8414da11d3b2b49ed3d8328285cdcac5458 Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 16:45:15 -0700 Subject: [PATCH 08/12] ahb: put signals in the order they appear in signal traces in the spec --- junctions/src/main/scala/hasti.scala | 42 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 19726cf1..cf5a319b 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -59,16 +59,16 @@ abstract class HastiBundle(implicit val p: Parameters) extends ParameterizedBund with HasHastiParameters class HastiMasterIO(implicit p: Parameters) extends HastiBundle()(p) { - val haddr = UInt(OUTPUT, hastiAddrBits) - val hwrite = Bool(OUTPUT) - val hsize = UInt(OUTPUT, SZ_HSIZE) - val hburst = UInt(OUTPUT, SZ_HBURST) - val hprot = UInt(OUTPUT, SZ_HPROT) val htrans = UInt(OUTPUT, SZ_HTRANS) val hmastlock = Bool(OUTPUT) + val haddr = UInt(OUTPUT, hastiAddrBits) + val hwrite = Bool(OUTPUT) + val hburst = UInt(OUTPUT, SZ_HBURST) + val hsize = UInt(OUTPUT, SZ_HSIZE) + val hprot = UInt(OUTPUT, SZ_HPROT) val hwdata = Bits(OUTPUT, hastiDataBits) - val hrdata = Bits(INPUT, hastiDataBits) + val hrdata = Bits(INPUT, hastiDataBits) val hready = Bool(INPUT) val hresp = UInt(INPUT, SZ_HRESP) @@ -79,15 +79,15 @@ class HastiMasterIO(implicit p: Parameters) extends HastiBundle()(p) { } class HastiSlaveIO(implicit p: Parameters) extends HastiBundle()(p) { - val haddr = UInt(INPUT, hastiAddrBits) - val hwrite = Bool(INPUT) - val hsize = UInt(INPUT, SZ_HSIZE) - val hburst = UInt(INPUT, SZ_HBURST) - val hprot = UInt(INPUT, SZ_HPROT) val htrans = UInt(INPUT, SZ_HTRANS) val hmastlock = Bool(INPUT) + val haddr = UInt(INPUT, hastiAddrBits) + val hwrite = Bool(INPUT) + val hburst = UInt(INPUT, SZ_HBURST) + val hsize = UInt(INPUT, SZ_HSIZE) + val hprot = UInt(INPUT, SZ_HPROT) - val hwdata = Bits(INPUT, hastiDataBits) + val hwdata = Bits(INPUT, hastiDataBits) val hrdata = Bits(OUTPUT, hastiDataBits) val hsel = Bool(INPUT) @@ -319,21 +319,21 @@ class HastiSlaveMux(n: Int)(implicit p: Parameters) extends HastiModule()(p) { class HastiSlaveToMaster(implicit p: Parameters) extends HastiModule()(p) { val io = new Bundle { - val in = new HastiSlaveIO + val in = new HastiSlaveIO val out = new HastiMasterIO } - io.out.haddr := io.in.haddr - io.out.hwrite := io.in.hwrite - io.out.hsize := io.in.hsize - io.out.hburst := io.in.hburst - io.out.hprot := io.in.hprot - io.out.htrans := Mux(io.in.hsel && io.in.hreadyin, io.in.htrans, HTRANS_IDLE) + io.out.htrans := Mux(io.in.hsel && io.in.hreadyin, io.in.htrans, HTRANS_IDLE) io.out.hmastlock := io.in.hmastlock - io.out.hwdata := io.in.hwdata + io.out.haddr := io.in.haddr + io.out.hwrite := io.in.hwrite + io.out.hburst := io.in.hburst + io.out.hsize := io.in.hsize + io.out.hprot := io.in.hprot + io.out.hwdata := io.in.hwdata io.in.hrdata := io.out.hrdata io.in.hready := io.out.hready - io.in.hresp := io.out.hresp + io.in.hresp := io.out.hresp } class HastiMasterIONastiIOConverter(implicit p: Parameters) extends HastiModule()(p) From 1db40687c6e55a3205ec81e6a2ce194ce1ad3e3c Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Wed, 18 May 2016 16:46:28 -0700 Subject: [PATCH 09/12] ahb: eliminate now-unnecesary non-standard hreadyin --- junctions/src/main/scala/hasti.scala | 3 +-- junctions/src/main/scala/poci.scala | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index cf5a319b..ed35d253 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -91,7 +91,6 @@ class HastiSlaveIO(implicit p: Parameters) extends HastiBundle()(p) { val hrdata = Bits(OUTPUT, hastiDataBits) val hsel = Bool(INPUT) - val hreadyin = Bool(INPUT) // !!! non-standard signal val hready = Bool(OUTPUT) val hresp = UInt(OUTPUT, SZ_HRESP) } @@ -323,7 +322,7 @@ class HastiSlaveToMaster(implicit p: Parameters) extends HastiModule()(p) { val out = new HastiMasterIO } - io.out.htrans := Mux(io.in.hsel && io.in.hreadyin, io.in.htrans, HTRANS_IDLE) + io.out.htrans := Mux(io.in.hsel, io.in.htrans, HTRANS_IDLE) io.out.hmastlock := io.in.hmastlock io.out.haddr := io.in.haddr io.out.hwrite := io.in.hwrite diff --git a/junctions/src/main/scala/poci.scala b/junctions/src/main/scala/poci.scala index b4248fe2..ac089164 100644 --- a/junctions/src/main/scala/poci.scala +++ b/junctions/src/main/scala/poci.scala @@ -23,7 +23,7 @@ class HastiToPociBridge(implicit p: Parameters) extends HastiModule()(p) { val s_idle :: s_setup :: s_access :: Nil = Enum(UInt(), 3) val state = Reg(init = s_idle) - val transfer = io.in.hsel & io.in.hreadyin & io.in.htrans(1) + val transfer = io.in.hsel & io.in.htrans(1) switch (state) { is (s_idle) { From e1e8eda41900ab621eb076a698c402128c6f4a1a Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 24 May 2016 14:06:57 -0700 Subject: [PATCH 10/12] ahb: add a test SRAM --- junctions/src/main/scala/hasti.scala | 85 ++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index ed35d253..3bb58347 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -51,6 +51,8 @@ trait HasHastiParameters { val hastiParams = p(HastiKey(p(HastiId))) val hastiAddrBits = hastiParams.addrBits val hastiDataBits = hastiParams.dataBits + val hastiDataBytes = hastiDataBits/8 + val hastiAlignment = log2Up(hastiDataBytes) } abstract class HastiModule(implicit val p: Parameters) extends Module @@ -420,3 +422,86 @@ class HastiMasterIONastiIOConverter(implicit p: Parameters) extends HastiModule( when (len === UInt(0)) { state := s_idle } } } + +class HastiTestSRAM(depth: Int)(implicit p: Parameters) extends HastiModule()(p) { + val io = new HastiSlaveIO + + // This is a test SRAM with random delays + val ready = LFSR16(Bool(true))(0) // Bool(true) + + // Calculate the bitmask of which bytes are being accessed + val mask_decode = Vec.tabulate(hastiAlignment+1) (UInt(_) <= io.hsize) + val mask_wide = Vec.tabulate(hastiDataBytes) { i => mask_decode(log2Up(i+1)) } + val mask_shift = mask_wide.toBits().asUInt() << io.haddr(hastiAlignment-1,0) + + // The request had better have been aligned! (AHB-lite requires this) + assert ((io.haddr & mask_decode.toBits()(hastiAlignment,1).asUInt) === UInt(0)) + + // The mask and address during the address phase + val a_request = io.hsel && (io.htrans === HTRANS_NONSEQ || io.htrans === HTRANS_SEQ) + val a_mask = mask_shift(hastiDataBytes-1, 0) + val a_address = io.haddr >> UInt(hastiAlignment) + val a_write = io.hwrite + + // The data phase signals + val d_read = RegEnable(a_request && !a_write, Bool(false), ready) + val d_mask = RegEnable(a_mask, ready && a_request) + val d_wdata = Vec.tabulate(hastiDataBytes) { i => io.hwdata(8*(i+1)-1, 8*i) } + + // AHB writes must occur during the data phase; this poses a structural + // hazard with reads which must occur during the address phase. To solve + // this problem, we delay the writes until there is a free cycle. + // + // The idea is to record the address information from address phase and + // then as soon as possible flush the pending write. This cannot be done + // on a cycle when there is an address phase read, but on any other cycle + // the write will execute. In the case of reads following a write, the + // result must bypass data from the pending write into the read if they + // happen to have matching address. + + // Remove this once HoldUnless is in chisel3 + def holdUnless[T <: Data](in : T, enable: Bool): T = Mux(!enable, RegEnable(in, enable), in) + + // Pending write? + val p_valid = RegInit(Bool(false)) + val p_address = Reg(a_address) + val p_mask = Reg(a_mask) + val p_latch_d = RegNext(ready && a_request && a_write, Bool(false)) + val p_wdata = holdUnless(d_wdata, p_latch_d) + + // Use single-ported memory with byte-write enable + val mem = SeqMem(depth, Vec(hastiDataBytes, Bits(width = 8))) + + // Decide is the SRAM port is used for reading or (potentially) writing + val read = ready && a_request && !a_write + // In case we are stalled, we need to hold the read data + val d_rdata = holdUnless(mem.read(a_address, read), RegNext(read)) + // Whenever the port is not needed for reading, execute pending writes + when (!read) { + when (p_valid) { mem.write(p_address, p_wdata, p_mask.toBools) } + p_valid := Bool(false) + } + + // Record the request for later? + when (ready && a_request && a_write) { + p_valid := Bool(true) + p_address := a_address + p_mask := a_mask + } + + // Does the read need to be muxed with the previous write? + val a_bypass = a_address === p_address && p_valid + val d_bypass = RegEnable(a_bypass, ready && a_request) + + // Mux in data from the pending write + val muxdata = Vec((p_mask.toBools zip (p_wdata zip d_rdata)) + map { case (m, (p, r)) => Mux(d_bypass && m, p, r) }) + // Wipe out any data the master should not see (for testing) + val outdata = Vec((d_mask.toBools zip muxdata) + map { case (m, p) => Mux(d_read && ready && m, p, Bits(0)) }) + + // Finally, the outputs + io.hrdata := outdata.toBits() + io.hready := ready + io.hresp := HRESP_OKAY +} From 200c69c106c6b3ab9e28008e4ec6142198996fca Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 24 May 2016 13:28:06 -0700 Subject: [PATCH 11/12] ahb: support hmastlock acquistion of crossbar --- junctions/src/main/scala/hasti.scala | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index 3bb58347..e2d47ed1 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -171,7 +171,6 @@ class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool])(implicit p: Paramete } val nSlaves = addressMap.size - // !!! handle hmastlock // Setup diversions infront of each master val diversions = Seq.tabulate(nMasters) { m => Module(new MasterDiversion) } @@ -181,6 +180,10 @@ class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool])(implicit p: Paramete val masters = diversions map (_.io.out) val slaves = io.slaves + // Lock status of the crossbar + val lockedM = Reg(init = Vec.fill(nMasters)(Bool(false))) + val isLocked = lockedM.reduce(_ || _) + // This matrix governs the master-slave connections in the address phase // It is indexed by addressPhaseGrantSM(slave)(master) // It is guaranteed to have at most one 'true' per column and per row @@ -247,11 +250,12 @@ class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool])(implicit p: Paramete .reduce(_ || _) } // Requested access to slaves from masters (pre-arbitration) + // NOTE: quash any request that requires bus ownership or conflicts with isLocked // NOTE: isNSeq does NOT include SEQ; thus, masters who are midburst do not // request access to a new slave. They stay tied to the old and do not get two. // NOTE: if a master was waited, it must repeat the same request as last cycle; // thus, it will request the same slave and not end up with two (unless buggy). - val NSeq = Vec(masters.map(_.isNSeq())) + val NSeq = Vec((lockedM zip masters) map { case(l, m) => m.isNSeq() && ((!isLocked && !m.hmastlock) || l) }) val requestSM = Vec.tabulate(nSlaves) { s => Vec.tabulate(nMasters) { m => matchMS(m)(s) && NSeq(m) && !bubbleM(m) } } // Select at most one master request per slave (lowest index = highest priority) @@ -281,7 +285,7 @@ class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool])(implicit p: Paramete (slaves zip addressPhaseGrantSM) foreach { case (s, g) => { s.htrans := Mux1H(g, masters.map(_.htrans)) // defaults to HTRANS_IDLE (0) s.haddr := Mux1H(g, masters.map(_.haddr)) - s.hmastlock := Mux1H(g, masters.map(_.hmastlock)) // !!! use global crossbar lock state + s.hmastlock := isLocked s.hwrite := Mux1H(g, masters.map(_.hwrite)) s.hsize := Mux1H(g, masters.map(_.hsize)) s.hburst := Mux1H(g, masters.map(_.hburst)) @@ -293,6 +297,20 @@ class HastiXbar(nMasters: Int, addressMap: Seq[UInt=>Bool])(implicit p: Paramete (slaves zip dataPhaseGrantSM) foreach { case (s, g) => { s.hwdata := Mux1H(g, masters.map(_.hwdata)) } } + + // When no master-slave connections are active, a master can take-over the bus + val canLock = !addressPhaseGrantSM.map({ v => v.reduce(_ || _) }).reduce(_ || _) + + // Lowest index highest priority for lock arbitration + val reqLock = masters.map(_.hmastlock) + val winLock = PriorityEncoderOH(reqLock) + + // Lock arbitration + when (isLocked) { + lockedM := (lockedM zip reqLock) map { case (a,b) => a && b } + } .elsewhen (canLock) { + lockedM := winLock + } } class HastiBus(amap: Seq[UInt=>Bool])(implicit p: Parameters) extends HastiModule()(p) { From b921bae1070a0ba0054aefa73b3996ac104f286f Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Tue, 24 May 2016 14:14:57 -0700 Subject: [PATCH 12/12] ahb: eliminate trait abused for constants --- junctions/src/main/scala/hasti.scala | 4 +++- junctions/src/main/scala/package.scala | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/junctions/src/main/scala/hasti.scala b/junctions/src/main/scala/hasti.scala index e2d47ed1..53637419 100644 --- a/junctions/src/main/scala/hasti.scala +++ b/junctions/src/main/scala/hasti.scala @@ -3,7 +3,7 @@ package junctions import Chisel._ import cde.{Parameters, Field} -trait HastiConstants +object HastiConstants { // Values for htrans val SZ_HTRANS = 2 @@ -42,6 +42,8 @@ trait HastiConstants def dgate(valid: Bool, b: UInt) = Fill(b.getWidth, valid) & b } +import HastiConstants._ + case class HastiParameters(dataBits: Int, addrBits: Int) case object HastiId extends Field[String] case class HastiKey(id: String) extends Field[HastiParameters] diff --git a/junctions/src/main/scala/package.scala b/junctions/src/main/scala/package.scala index 317b7109..214a91e5 100644 --- a/junctions/src/main/scala/package.scala +++ b/junctions/src/main/scala/package.scala @@ -1 +1 @@ -package object junctions extends HastiConstants +package object junctions