Merge pull request #912 from freechipsproject/add-mask-rom
tilelink: add mask rom
This commit is contained in:
commit
f988b91575
@ -1,6 +1,7 @@
|
|||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
|
|
||||||
# See LICENSE for license details.
|
# See LICENSE.SiFive for license details.
|
||||||
|
# See LICENSE.Berkeley for license details.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import math
|
import math
|
138
scripts/vlsi_rom_gen
Executable file
138
scripts/vlsi_rom_gen
Executable file
@ -0,0 +1,138 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
# See LICENSE.SiFive for license details.
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import doctest
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
verilog_template = """
|
||||||
|
module {name}(
|
||||||
|
input clock,
|
||||||
|
input oe,
|
||||||
|
input me,
|
||||||
|
input [{address_bits_minus_1}:0] address,
|
||||||
|
output [{output_width_minus_1}:0] q
|
||||||
|
);
|
||||||
|
reg [{output_width_minus_1}:0] out;
|
||||||
|
reg [{output_width_minus_1}:0] rom [0:{depth_minus_1}];
|
||||||
|
|
||||||
|
|
||||||
|
// 1024 is the maximum length of $readmemh filename supported by Cadence Incisive
|
||||||
|
reg [1024 * 8 - 1:0] path;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
initial begin
|
||||||
|
`ifdef RANDOMIZE
|
||||||
|
`ifdef RANDOMIZE_MEM_INIT
|
||||||
|
for (i = 0; i < {depth}; i++) begin
|
||||||
|
rom[i] = {{{num_random_blocks}{{$random}}}};
|
||||||
|
end
|
||||||
|
`endif
|
||||||
|
`endif
|
||||||
|
if (!$value$plusargs("maskromhex=%s", path)) begin
|
||||||
|
path = "{rom_hex_file}";
|
||||||
|
end
|
||||||
|
$readmemh(path, rom);
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
always @(posedge clock) begin
|
||||||
|
if (me) begin
|
||||||
|
out <= rom[address];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign q = oe ? out : {output_width}'bZ;
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def gen_rom(name, width, depth, rom_hex_file):
|
||||||
|
variables = {
|
||||||
|
'name': name,
|
||||||
|
'address_bits_minus_1': (depth - 1).bit_length() - 1,
|
||||||
|
'depth': depth,
|
||||||
|
'depth_minus_1': depth - 1,
|
||||||
|
'output_width': width,
|
||||||
|
'output_width_minus_1': width - 1,
|
||||||
|
# $random in verilog returns 32 bits; compute how many times to repeat
|
||||||
|
# $random in order to fill the width
|
||||||
|
'num_random_blocks': (width - 1) // 32 + 1,
|
||||||
|
'rom_hex_file': rom_hex_file,
|
||||||
|
}
|
||||||
|
return verilog_template.format(**variables)
|
||||||
|
|
||||||
|
|
||||||
|
def iterate_by_n(it, n):
|
||||||
|
"""Iterate over items in it, yielding n-tuples of successive items.
|
||||||
|
|
||||||
|
>>> list(iterate_by_n([1, 2, 3, 4, 5, 6], n=2))
|
||||||
|
[(1, 2), (3, 4), (5, 6)]
|
||||||
|
>>> list(iterate_by_n([1, 2, 3, 4, 5, 6], n=3))
|
||||||
|
[(1, 2, 3), (4, 5, 6)]
|
||||||
|
>>> list(iterate_by_n([1, 2, 3, 4, 5, 6], n=4))
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: Iterable length not evenly divisible by 4
|
||||||
|
"""
|
||||||
|
it = iter(it)
|
||||||
|
while True:
|
||||||
|
batch = ()
|
||||||
|
for i in range(n):
|
||||||
|
try:
|
||||||
|
batch += (next(it),)
|
||||||
|
except StopIteration:
|
||||||
|
if batch: # If this is not the first iteration
|
||||||
|
raise ValueError(
|
||||||
|
'Iterable length not evenly divisible by {}'.format(n)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
yield batch
|
||||||
|
|
||||||
|
|
||||||
|
def try_cast_int(x):
|
||||||
|
try:
|
||||||
|
return int(x)
|
||||||
|
except ValueError:
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
ROMParameters = namedtuple('ROMParameters', ['name', 'depth', 'width'])
|
||||||
|
default_rom_parameters = ROMParameters(name='', depth=0, width=0)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_line(line):
|
||||||
|
kwargs = {key: try_cast_int(val)
|
||||||
|
for key, val in iterate_by_n(line.split(), 2)}
|
||||||
|
rom_parameters = default_rom_parameters._replace(**kwargs)
|
||||||
|
return rom_parameters._asdict()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if '--run-tests' in sys.argv:
|
||||||
|
(failures, total) = doctest.testmod(verbose=True)
|
||||||
|
sys.exit(1 if failures else 0)
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.exit('Please give a .conf file as input')
|
||||||
|
|
||||||
|
print('// This file created by ' + __file__)
|
||||||
|
with open(sys.argv[1]) as fp:
|
||||||
|
lines = fp.readlines()
|
||||||
|
if len(lines) > 1:
|
||||||
|
warnings.warn('vlsi_rom_gen detected multiple ROMs. ROM contents will be duplicated.')
|
||||||
|
for line in lines:
|
||||||
|
verilog = gen_rom(rom_hex_file=sys.argv[2],
|
||||||
|
**parse_line(line))
|
||||||
|
print(verilog)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -1,4 +1,4 @@
|
|||||||
// See LICENSE for license details.
|
// See LICENSE.SiFive for license details.
|
||||||
|
|
||||||
package freechips.rocketchip.coreplex
|
package freechips.rocketchip.coreplex
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ import Chisel._
|
|||||||
import freechips.rocketchip.config.{Field, Parameters}
|
import freechips.rocketchip.config.{Field, Parameters}
|
||||||
import freechips.rocketchip.coreplex._
|
import freechips.rocketchip.coreplex._
|
||||||
import freechips.rocketchip.diplomacy._
|
import freechips.rocketchip.diplomacy._
|
||||||
|
import freechips.rocketchip.tilelink._
|
||||||
|
import freechips.rocketchip.util._
|
||||||
|
|
||||||
import java.nio.{ByteBuffer, ByteOrder}
|
import java.nio.{ByteBuffer, ByteOrder}
|
||||||
import java.nio.file.{Files, Paths}
|
import java.nio.file.{Files, Paths}
|
||||||
|
|
||||||
@ -17,6 +20,47 @@ case class BootROMParams(
|
|||||||
contentFileName: String)
|
contentFileName: String)
|
||||||
case object BootROMParams extends Field[BootROMParams]
|
case object BootROMParams extends Field[BootROMParams]
|
||||||
|
|
||||||
|
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4,
|
||||||
|
resources: Seq[Resource] = new SimpleDevice("rom", Seq("sifive,rom0")).reg("mem"))(implicit p: Parameters) extends LazyModule
|
||||||
|
{
|
||||||
|
val node = TLManagerNode(beatBytes, TLManagerParameters (
|
||||||
|
address = List(AddressSet(base, size-1)),
|
||||||
|
resources = resources,
|
||||||
|
regionType = RegionType.UNCACHED,
|
||||||
|
executable = executable,
|
||||||
|
supportsGet = TransferSizes(1, beatBytes),
|
||||||
|
fifoId = Some(0)))
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
}
|
||||||
|
|
||||||
|
val contents = contentsDelayed
|
||||||
|
val wrapSize = 1 << log2Ceil(contents.size)
|
||||||
|
require (wrapSize <= size)
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val edge = node.edgesIn(0)
|
||||||
|
|
||||||
|
val words = (contents ++ Seq.fill(wrapSize-contents.size)(0.toByte)).grouped(beatBytes).toSeq
|
||||||
|
val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8})
|
||||||
|
val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes)))
|
||||||
|
|
||||||
|
in.d.valid := in.a.valid
|
||||||
|
in.a.ready := in.d.ready
|
||||||
|
|
||||||
|
val index = in.a.bits.address(log2Ceil(wrapSize)-1,log2Ceil(beatBytes))
|
||||||
|
val high = if (wrapSize == size) UInt(0) else in.a.bits.address(log2Ceil(size)-1, log2Ceil(wrapSize))
|
||||||
|
in.d.bits := edge.AccessAck(in.a.bits, Mux(high.orR, UInt(0), rom(index)))
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Adds a boot ROM that contains the DTB describing the system's coreplex. */
|
/** Adds a boot ROM that contains the DTB describing the system's coreplex. */
|
||||||
trait HasPeripheryBootROM extends HasPeripheryBus {
|
trait HasPeripheryBootROM extends HasPeripheryBus {
|
||||||
val dtb: DTB
|
val dtb: DTB
|
||||||
|
71
src/main/scala/devices/tilelink/MaskROM.scala
Normal file
71
src/main/scala/devices/tilelink/MaskROM.scala
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// See LICENSE.SiFive for license details.
|
||||||
|
|
||||||
|
package freechips.rocketchip.devices.tilelink
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import freechips.rocketchip.coreplex.{HasPeripheryBus}
|
||||||
|
import freechips.rocketchip.config.{Field, Parameters}
|
||||||
|
import freechips.rocketchip.diplomacy._
|
||||||
|
import freechips.rocketchip.tilelink._
|
||||||
|
import freechips.rocketchip.util._
|
||||||
|
|
||||||
|
case class MaskROMParams(address: BigInt, name: String, depth: Int = 2048, width: Int = 32)
|
||||||
|
|
||||||
|
case object PeripheryMaskROMKey extends Field[Seq[MaskROMParams]]
|
||||||
|
|
||||||
|
trait HasPeripheryMaskROMSlave extends HasPeripheryBus {
|
||||||
|
val maskROMParams = p(PeripheryMaskROMKey)
|
||||||
|
val maskROMs = maskROMParams map { params =>
|
||||||
|
val maskROM = LazyModule(new TLMaskROM(params))
|
||||||
|
maskROM.node := pbus.toFixedWidthSingleBeatSlave(maskROM.beatBytes)
|
||||||
|
maskROM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TLMaskROM(c: MaskROMParams)(implicit p: Parameters) extends LazyModule {
|
||||||
|
val beatBytes = c.width/8
|
||||||
|
val node = TLManagerNode(beatBytes, TLManagerParameters(
|
||||||
|
address = AddressSet.misaligned(c.address, c.depth*beatBytes),
|
||||||
|
resources = new SimpleDevice("rom", Seq("sifive,maskrom0")).reg("mem"),
|
||||||
|
regionType = RegionType.UNCACHED,
|
||||||
|
executable = true,
|
||||||
|
supportsGet = TransferSizes(1, beatBytes),
|
||||||
|
fifoId = Some(0))) // requests are handled in order
|
||||||
|
|
||||||
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = node.bundleIn
|
||||||
|
}
|
||||||
|
|
||||||
|
val in = io.in(0)
|
||||||
|
val edge = node.edgesIn(0)
|
||||||
|
|
||||||
|
val rom = ROMGenerator(ROMConfig(c.name, c.depth, c.width))
|
||||||
|
rom.io.clock := clock
|
||||||
|
rom.io.address := edge.addr_hi(in.a.bits.address - UInt(c.address))(log2Ceil(c.depth)-1, 0)
|
||||||
|
rom.io.oe := Bool(true) // active high tri state enable
|
||||||
|
rom.io.me := in.a.fire()
|
||||||
|
|
||||||
|
val d_full = RegInit(Bool(false))
|
||||||
|
val d_size = Reg(UInt())
|
||||||
|
val d_source = Reg(UInt())
|
||||||
|
|
||||||
|
// Flow control
|
||||||
|
when (in.d.fire()) { d_full := Bool(false) }
|
||||||
|
when (in.a.fire()) { d_full := Bool(true) }
|
||||||
|
in.d.valid := d_full
|
||||||
|
in.a.ready := in.d.ready || !d_full
|
||||||
|
|
||||||
|
when (in.a.fire()) {
|
||||||
|
d_size := in.a.bits.size
|
||||||
|
d_source := in.a.bits.source
|
||||||
|
}
|
||||||
|
|
||||||
|
in.d.bits := edge.AccessAck(d_source, d_size, rom.io.q)
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
}
|
||||||
|
}
|
@ -1,52 +0,0 @@
|
|||||||
// See LICENSE.SiFive for license details.
|
|
||||||
// See LICENSE.Berkeley for license details.
|
|
||||||
|
|
||||||
package freechips.rocketchip.devices.tilelink
|
|
||||||
|
|
||||||
import Chisel._
|
|
||||||
import freechips.rocketchip.config.Parameters
|
|
||||||
import freechips.rocketchip.diplomacy._
|
|
||||||
import freechips.rocketchip.tilelink._
|
|
||||||
import freechips.rocketchip.util._
|
|
||||||
|
|
||||||
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4,
|
|
||||||
resources: Seq[Resource] = new SimpleDevice("rom", Seq("sifive,rom0")).reg("mem"))(implicit p: Parameters) extends LazyModule
|
|
||||||
{
|
|
||||||
|
|
||||||
val node = TLManagerNode(beatBytes, TLManagerParameters (
|
|
||||||
address = List(AddressSet(base, size-1)),
|
|
||||||
resources = resources,
|
|
||||||
regionType = RegionType.UNCACHED,
|
|
||||||
executable = executable,
|
|
||||||
supportsGet = TransferSizes(1, beatBytes),
|
|
||||||
fifoId = Some(0)))
|
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
|
||||||
val io = new Bundle {
|
|
||||||
val in = node.bundleIn
|
|
||||||
}
|
|
||||||
|
|
||||||
val contents = contentsDelayed
|
|
||||||
val wrapSize = 1 << log2Ceil(contents.size)
|
|
||||||
require (wrapSize <= size)
|
|
||||||
|
|
||||||
val in = io.in(0)
|
|
||||||
val edge = node.edgesIn(0)
|
|
||||||
|
|
||||||
val words = (contents ++ Seq.fill(wrapSize-contents.size)(0.toByte)).grouped(beatBytes).toSeq
|
|
||||||
val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8})
|
|
||||||
val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes)))
|
|
||||||
|
|
||||||
in.d.valid := in.a.valid
|
|
||||||
in.a.ready := in.d.ready
|
|
||||||
|
|
||||||
val index = in.a.bits.address(log2Ceil(wrapSize)-1,log2Ceil(beatBytes))
|
|
||||||
val high = if (wrapSize == size) UInt(0) else in.a.bits.address(log2Ceil(size)-1, log2Ceil(wrapSize))
|
|
||||||
in.d.bits := edge.AccessAck(in.a.bits, Mux(high.orR, UInt(0), rom(index)))
|
|
||||||
|
|
||||||
// Tie off unused channels
|
|
||||||
in.b.valid := Bool(false)
|
|
||||||
in.c.ready := Bool(true)
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
}
|
|
||||||
}
|
|
@ -88,5 +88,6 @@ object Generator extends GeneratorApp {
|
|||||||
val longName = names.topModuleProject + "." + names.configs
|
val longName = names.topModuleProject + "." + names.configs
|
||||||
generateFirrtl
|
generateFirrtl
|
||||||
generateTestSuiteMakefrags
|
generateTestSuiteMakefrags
|
||||||
|
generateROMs
|
||||||
generateArtefacts
|
generateArtefacts
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,21 @@ trait HasGeneratorUtilities {
|
|||||||
Driver.elaborate(gen)
|
Driver.elaborate(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def enumerateROMs(circuit: Circuit): String = {
|
||||||
|
val res = new StringBuilder
|
||||||
|
val configs =
|
||||||
|
circuit.components flatMap { m =>
|
||||||
|
m.id match {
|
||||||
|
case rom: BlackBoxedROM => Some((rom.name, ROMGenerator.lookup(rom)))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configs foreach { case (name, c) =>
|
||||||
|
res append s"name ${name} depth ${c.depth} width ${c.width}\n"
|
||||||
|
}
|
||||||
|
res.toString
|
||||||
|
}
|
||||||
|
|
||||||
def writeOutputFile(targetDir: String, fname: String, contents: String): File = {
|
def writeOutputFile(targetDir: String, fname: String, contents: String): File = {
|
||||||
val f = new File(targetDir, fname)
|
val f = new File(targetDir, fname)
|
||||||
val fw = new FileWriter(f)
|
val fw = new FileWriter(f)
|
||||||
@ -103,6 +118,10 @@ trait GeneratorApp extends App with HasGeneratorUtilities {
|
|||||||
TestGeneration.addSuite(DefaultTestSuites.singleRegression)
|
TestGeneration.addSuite(DefaultTestSuites.singleRegression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def generateROMs {
|
||||||
|
writeOutputFile(td, s"$longName.rom.conf", enumerateROMs(circuit))
|
||||||
|
}
|
||||||
|
|
||||||
/** Output files created as a side-effect of elaboration */
|
/** Output files created as a side-effect of elaboration */
|
||||||
def generateArtefacts {
|
def generateArtefacts {
|
||||||
ElaborationArtefacts.files.foreach { case (extension, contents) =>
|
ElaborationArtefacts.files.foreach { case (extension, contents) =>
|
||||||
|
35
src/main/scala/util/ROMGenerator.scala
Normal file
35
src/main/scala/util/ROMGenerator.scala
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// See LICENSE.SiFive for license details.
|
||||||
|
|
||||||
|
package freechips.rocketchip.util
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import scala.collection.mutable.{HashMap}
|
||||||
|
|
||||||
|
case class ROMConfig(name: String, depth: Int, width: Int)
|
||||||
|
|
||||||
|
class BlackBoxedROM(c: ROMConfig) extends BlackBox {
|
||||||
|
val io = new Bundle {
|
||||||
|
val clock = Clock(INPUT)
|
||||||
|
val address = UInt(INPUT, log2Ceil(c.depth))
|
||||||
|
val oe = Bool(INPUT)
|
||||||
|
val me = Bool(INPUT)
|
||||||
|
val q = UInt(OUTPUT, c.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def desiredName: String = c.name
|
||||||
|
}
|
||||||
|
|
||||||
|
object ROMGenerator {
|
||||||
|
private var finalized = false
|
||||||
|
private val roms = HashMap[BlackBoxedROM, ROMConfig]()
|
||||||
|
def apply(c: ROMConfig): BlackBoxedROM = {
|
||||||
|
require(!finalized)
|
||||||
|
val m = Module(new BlackBoxedROM(c))
|
||||||
|
roms(m) = c
|
||||||
|
m
|
||||||
|
}
|
||||||
|
def lookup(m: BlackBoxedROM): ROMConfig = {
|
||||||
|
finalized = true
|
||||||
|
roms(m)
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ default: all
|
|||||||
|
|
||||||
base_dir = $(abspath ..)
|
base_dir = $(abspath ..)
|
||||||
generated_dir = $(abspath ./generated-src)
|
generated_dir = $(abspath ./generated-src)
|
||||||
VLSI_MEM_GEN ?= $(base_dir)/vsim/vlsi_mem_gen
|
VLSI_MEM_GEN ?= $(base_dir)/scripts/vlsi_mem_gen
|
||||||
mem_gen = $(VLSI_MEM_GEN)
|
mem_gen = $(VLSI_MEM_GEN)
|
||||||
sim_dir = .
|
sim_dir = .
|
||||||
output_dir = $(sim_dir)/output
|
output_dir = $(sim_dir)/output
|
||||||
|
Loading…
Reference in New Issue
Block a user