1
0
Fork 0

tilelink: add mask rom

This commit is contained in:
Yunsup Lee 2017-07-31 21:12:45 -07:00
parent 4b33249812
commit 6ef8ee5d4d
10 changed files with 312 additions and 55 deletions

View File

@ -1,6 +1,7 @@
#! /usr/bin/env python
# See LICENSE for license details.
# See LICENSE.SiFive for license details.
# See LICENSE.Berkeley for license details.
import sys
import math

138
scripts/vlsi_rom_gen Executable file
View 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()

View File

@ -1,4 +1,4 @@
// See LICENSE for license details.
// See LICENSE.SiFive for license details.
package freechips.rocketchip.coreplex

View File

@ -6,6 +6,9 @@ import Chisel._
import freechips.rocketchip.config.{Field, Parameters}
import freechips.rocketchip.coreplex._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
import java.nio.{ByteBuffer, ByteOrder}
import java.nio.file.{Files, Paths}
@ -17,6 +20,47 @@ case class BootROMParams(
contentFileName: String)
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. */
trait HasPeripheryBootROM extends HasPeripheryBus {
val dtb: DTB

View 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)
}
}

View File

@ -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)
}
}

View File

@ -88,5 +88,6 @@ object Generator extends GeneratorApp {
val longName = names.topModuleProject + "." + names.configs
generateFirrtl
generateTestSuiteMakefrags
generateROMs
generateArtefacts
}

View File

@ -53,6 +53,21 @@ trait HasGeneratorUtilities {
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 = {
val f = new File(targetDir, fname)
val fw = new FileWriter(f)
@ -103,6 +118,10 @@ trait GeneratorApp extends App with HasGeneratorUtilities {
TestGeneration.addSuite(DefaultTestSuites.singleRegression)
}
def generateROMs {
writeOutputFile(td, s"$longName.rom.conf", enumerateROMs(circuit))
}
/** Output files created as a side-effect of elaboration */
def generateArtefacts {
ElaborationArtefacts.files.foreach { case (extension, contents) =>

View 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)
}
}

View File

@ -11,7 +11,7 @@ default: all
base_dir = $(abspath ..)
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)
sim_dir = .
output_dir = $(sim_dir)/output