This commit is contained in:
16
.github/workflows/check-prop.yml
vendored
Normal file
16
.github/workflows/check-prop.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: "check properties"
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@v27
|
||||||
|
with:
|
||||||
|
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Setup environment
|
||||||
|
run: nix develop --command make verilog-wrappers verilog-props
|
||||||
|
- name: Run property checks
|
||||||
|
run: nix develop --command make check-prop
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015-2019 Jonathan Woodruff
|
* Copyright (c) 2015-2019 Jonathan Woodruff
|
||||||
* Copyright (c) 2017-2021 Alexandre Joannou
|
* Copyright (c) 2017-2025 Alexandre Joannou
|
||||||
* Copyright (c) 2019 Peter Rugg
|
* Copyright (c) 2019 Peter Rugg
|
||||||
* Copyright (c) 2021 Dapeng Gao
|
* Copyright (c) 2021 Dapeng Gao
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
@@ -355,7 +355,7 @@ function BoundsInfo#(CapAddrW) getBoundsInfoFat (CapFat cap, TempFields tf)
|
|||||||
Bit #(TAdd #(MW, 2)) correctTop = {pack (tf.topCorrection), topBits};
|
Bit #(TAdd #(MW, 2)) correctTop = {pack (tf.topCorrection), topBits};
|
||||||
// Get the length by subtracting base from top and shifting appropriately, and
|
// Get the length by subtracting base from top and shifting appropriately, and
|
||||||
// saturate in case of big exponent
|
// saturate in case of big exponent
|
||||||
CapAddrPlus1 length =
|
CapAddr length =
|
||||||
(exp >= resetExp) ? ~0 : zeroExtend (correctTop - correctBase) << exp;
|
(exp >= resetExp) ? ~0 : zeroExtend (correctTop - correctBase) << exp;
|
||||||
|
|
||||||
// compute repBase
|
// compute repBase
|
||||||
@@ -446,12 +446,12 @@ function CapAddrPlus1 getTopFat(CapFat cap, TempFields tf);
|
|||||||
ret[valueOf(CapAddrW)] = ~ret[valueOf(CapAddrW)];
|
ret[valueOf(CapAddrW)] = ~ret[valueOf(CapAddrW)];
|
||||||
return ret;
|
return ret;
|
||||||
endfunction
|
endfunction
|
||||||
function CapAddrPlus1 getLengthFat(CapFat cap, TempFields tf);
|
function CapAddr getLengthFat(CapFat cap, TempFields tf);
|
||||||
// Get the top and base bits with the 2 correction bits prepended
|
// Get the top and base bits with the 2 correction bits prepended
|
||||||
Bit#(TAdd#(MW,2)) top = {pack(tf.topCorrection),cap.bounds.topBits};
|
Bit#(TAdd#(MW,2)) top = {pack(tf.topCorrection),cap.bounds.topBits};
|
||||||
Bit#(TAdd#(MW,2)) base = {pack(tf.baseCorrection),cap.bounds.baseBits};
|
Bit#(TAdd#(MW,2)) base = {pack(tf.baseCorrection),cap.bounds.baseBits};
|
||||||
// Get the length by substracting base from top and shifting appropriately
|
// Get the length by substracting base from top and shifting appropriately
|
||||||
CapAddrPlus1 length = zeroExtend(top - base) << cap.bounds.exp;
|
CapAddr length = zeroExtend(top - base) << cap.bounds.exp;
|
||||||
// Return a saturated length in case of big exponent
|
// Return a saturated length in case of big exponent
|
||||||
// TODO: The saturation behaviour here is short of being correct
|
// TODO: The saturation behaviour here is short of being correct
|
||||||
return (cap.bounds.exp >= resetExp) ? ~0 : length;
|
return (cap.bounds.exp >= resetExp) ? ~0 : length;
|
||||||
@@ -679,6 +679,9 @@ function SetBoundsReturn#(CapFat, CapAddrW) setBoundsFat(CapFat cap, Address len
|
|||||||
|
|
||||||
Bool resultInBounds = newBaseInBounds && newTopInBounds && !addressWrap;
|
Bool resultInBounds = newBaseInBounds && newTopInBounds && !addressWrap;
|
||||||
|
|
||||||
|
// Nullify the capability if the result is not in bounds
|
||||||
|
if (!resultInBounds) ret.isCapability = False;
|
||||||
|
|
||||||
// Return derived capability
|
// Return derived capability
|
||||||
return SetBoundsReturn { cap: ret
|
return SetBoundsReturn { cap: ret
|
||||||
, exact: exact
|
, exact: exact
|
||||||
@@ -1126,8 +1129,8 @@ instance CHERICap #(CapMem, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3), Delta
|
|||||||
CapabilityInMemory cap = unpack (capMem);
|
CapabilityInMemory cap = unpack (capMem);
|
||||||
return pack (cap.address);
|
return pack (cap.address);
|
||||||
endfunction
|
endfunction
|
||||||
function fromMem = error ("fromMem not implemented for CapMem");
|
function fromMem(x) = unpack(pack(x));
|
||||||
function toMem = error ("toMem not implemented for CapMem");
|
function toMem(x) = unpack(pack(x));
|
||||||
|
|
||||||
// capability address/offset manipulation
|
// capability address/offset manipulation
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1528,7 +1531,11 @@ instance Cast#(function CapPipe f0(t y), function Bit#(CapAddrW) f1(t x));
|
|||||||
endinstance
|
endinstance
|
||||||
|
|
||||||
`ifdef CAP64
|
`ifdef CAP64
|
||||||
typedef 32 VA_Width;
|
// XXX TODO
|
||||||
|
// This is probably the wrong fix but allows the code to compile, and the
|
||||||
|
// code for CAP64 is not used anywhere.
|
||||||
|
// Need to consider what the right size is.
|
||||||
|
typedef 31 VA_Width;
|
||||||
`else
|
`else
|
||||||
typedef 48 VA_Width;
|
typedef 48 VA_Width;
|
||||||
`endif
|
`endif
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ typedef union tagged {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
Bit #(addrW) base;
|
Bit #(addrW) base;
|
||||||
Bit #(TAdd #(addrW, 1)) top;
|
Bit #(TAdd #(addrW, 1)) top;
|
||||||
Bit #(TAdd #(addrW, 1)) length;
|
Bit #(addrW) length;
|
||||||
Bit #(addrW) repBase;
|
Bit #(addrW) repBase;
|
||||||
Bit #(TAdd #(addrW, 1)) repTop;
|
Bit #(TAdd #(addrW, 1)) repTop;
|
||||||
Bit #(TAdd #(addrW, 1)) repLength;
|
Bit #(TAdd #(addrW, 1)) repLength;
|
||||||
@@ -259,7 +259,7 @@ typeclass CHERICap #( type capT // type of the CHERICap capability
|
|||||||
// Get the top
|
// Get the top
|
||||||
function Bit #(TAdd #(addrW, 1)) getTop (capT cap) = getBoundsInfo(cap).top;
|
function Bit #(TAdd #(addrW, 1)) getTop (capT cap) = getBoundsInfo(cap).top;
|
||||||
// Get the length
|
// Get the length
|
||||||
function Bit #(TAdd #(addrW, 1)) getLength (capT cap) =
|
function Bit #(addrW) getLength (capT cap) =
|
||||||
getBoundsInfo(cap).length;
|
getBoundsInfo(cap).length;
|
||||||
// Assertion that the capability's address is between its base and top
|
// Assertion that the capability's address is between its base and top
|
||||||
function Bool isInBounds (capT cap, Bool isTopIncluded) =
|
function Bool isInBounds (capT cap, Bool isTopIncluded) =
|
||||||
|
|||||||
@@ -204,4 +204,4 @@ function Bool prop_setBounds(CapAddr base, CapAddr len, CapAddr addr, CapAddr ne
|
|||||||
return forallCap(base, len, addr, prop);
|
return forallCap(base, len, addr, prop);
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Alexandre Joannou
|
* Copyright (c) 2019-2025 Alexandre Joannou
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This software was developed by SRI International and the University of
|
* This software was developed by SRI International and the University of
|
||||||
@@ -30,7 +30,15 @@ package CHERICapWrap;
|
|||||||
import CHERICap :: *;
|
import CHERICap :: *;
|
||||||
import CHERICC_Fat :: *;
|
import CHERICC_Fat :: *;
|
||||||
|
|
||||||
|
`ifndef CAPTYPE
|
||||||
`define CAPTYPE CapPipe
|
`define CAPTYPE CapPipe
|
||||||
|
`endif
|
||||||
|
|
||||||
|
function CapPipe capArg(`CAPTYPE cap) = cast(cap);
|
||||||
|
function `CAPTYPE capRet(CapPipe cap) = cast(cap);
|
||||||
|
function Exact#(`CAPTYPE) capExactRet(Exact#(CapPipe) e_cap) =
|
||||||
|
Exact { exact: e_cap.exact, value: cast(e_cap.value) };
|
||||||
|
|
||||||
`ifndef CAP64
|
`ifndef CAP64
|
||||||
`define W(name) wrap128_``name
|
`define W(name) wrap128_``name
|
||||||
`else
|
`else
|
||||||
@@ -48,41 +56,45 @@ function `CAPTYPE `W(setFlags) (`CAPTYPE cap, Bit#(FlagsW) flags) = setFlags(cap
|
|||||||
(* noinline *)
|
(* noinline *)
|
||||||
function HardPerms `W(getHardPerms) (`CAPTYPE cap) = getHardPerms(cap);
|
function HardPerms `W(getHardPerms) (`CAPTYPE cap) = getHardPerms(cap);
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function `CAPTYPE `W(setHardPerms) (`CAPTYPE cap, HardPerms hardperms) = setHardPerms(cap, hardperms);
|
function `CAPTYPE `W(setHardPerms) (`CAPTYPE cap, HardPerms hardperms) = capRet(setHardPerms(capArg(cap), hardperms));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function SoftPerms `W(getSoftPerms) (`CAPTYPE cap) = getSoftPerms(cap);
|
function SoftPerms `W(getSoftPerms) (`CAPTYPE cap) = getSoftPerms(capArg(cap));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function `CAPTYPE `W(setSoftPerms) (`CAPTYPE cap, SoftPerms softperms) = setSoftPerms(cap, softperms);
|
function `CAPTYPE `W(setSoftPerms) (`CAPTYPE cap, SoftPerms softperms) = capRet(setSoftPerms(capArg(cap), softperms));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bit#(31) `W(getPerms) (`CAPTYPE cap) = getPerms(cap);
|
function Bit#(31) `W(getPerms) (`CAPTYPE cap) = getPerms(capArg(cap));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function `CAPTYPE `W(setPerms) (`CAPTYPE cap, Bit#(31) perms) = setPerms(cap, perms);
|
function `CAPTYPE `W(setPerms) (`CAPTYPE cap, Bit#(31) perms) = capRet(setPerms(capArg(cap), perms));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Kind#(OTypeW) `W(getKind) (`CAPTYPE cap) = getKind(cap);
|
function Kind#(OTypeW) `W(getKind) (`CAPTYPE cap) = getKind(capArg(cap));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function `CAPTYPE `W(setKind) (`CAPTYPE cap, Kind#(OTypeW) kind) = setKind(cap, kind);
|
function `CAPTYPE `W(setKind) (`CAPTYPE cap, Kind#(OTypeW) kind) = capRet(setKind(capArg(cap), kind));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bit#(CapAddrW) `W(getAddr) (`CAPTYPE cap) = getAddr(cap);
|
function Bit#(CapAddrW) `W(getAddr) (`CAPTYPE cap) = getAddr(cap);
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Exact#(`CAPTYPE) `W(setAddr) (`CAPTYPE cap, Bit#(CapAddrW) addr) = setAddr(cap, addr);
|
function `CAPTYPE `W(setAddrUnsafe) (`CAPTYPE cap, Bit#(CapAddrW) addr) = capRet(setAddrUnsafe(capArg(cap), addr));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bit#(CapAddrW) `W(getOffset) (`CAPTYPE cap) = getOffset(cap);
|
function `CAPTYPE `W(addAddrUnsafe) (`CAPTYPE cap, Bit#(TSub #(MW, 3)) inc) = capRet(addAddrUnsafe(capArg(cap), inc));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Exact#(`CAPTYPE) `W(modifyOffset) (`CAPTYPE cap, Bit#(CapAddrW) offset, Bool doInc) = modifyOffset (cap, offset, doInc);
|
function Exact#(`CAPTYPE) `W(setAddr) (`CAPTYPE cap, Bit#(CapAddrW) addr) = capExactRet(setAddr(capArg(cap), addr));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Exact#(`CAPTYPE) `W(setOffset) (`CAPTYPE cap, Bit#(CapAddrW) offset) = setOffset (cap, offset);
|
function Bit#(CapAddrW) `W(getOffset) (`CAPTYPE cap) = getOffset(capArg(cap));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Exact#(`CAPTYPE) `W(incOffset) (`CAPTYPE cap, Bit#(CapAddrW) inc) = incOffset (cap, inc);
|
function Exact#(`CAPTYPE) `W(modifyOffset) (`CAPTYPE cap, Bit#(CapAddrW) offset, Bool doInc) = capExactRet(modifyOffset (capArg(cap), offset, doInc));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bit#(CapAddrW) `W(getBase) (`CAPTYPE cap) = getBase(cap);
|
function Exact#(`CAPTYPE) `W(setOffset) (`CAPTYPE cap, Bit#(CapAddrW) offset) = capExactRet(setOffset (capArg(cap), offset));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bit#(TAdd#(CapAddrW, 1)) `W(getTop) (`CAPTYPE cap) = getTop(cap);
|
function Exact#(`CAPTYPE) `W(incOffset) (`CAPTYPE cap, Bit#(CapAddrW) inc) = capExactRet(incOffset (capArg(cap), inc));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bit#(TAdd#(CapAddrW, 1)) `W(getLength) (`CAPTYPE cap) = getLength(cap);
|
function Bit#(CapAddrW) `W(getBase) (`CAPTYPE cap) = getBase(capArg(cap));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Bool `W(isInBounds) (`CAPTYPE cap, Bool isTopIncluded) = isInBounds(cap, isTopIncluded);
|
function Bit#(TAdd#(CapAddrW, 1)) `W(getTop) (`CAPTYPE cap) = getTop(capArg(cap));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Exact#(`CAPTYPE) `W(setBounds) (`CAPTYPE cap, Bit#(CapAddrW) length) = setBounds(cap, length);
|
function Bit#(CapAddrW) `W(getLength) (`CAPTYPE cap) = getLength(capArg(cap));
|
||||||
|
(* noinline *)
|
||||||
|
function Bool `W(isInBounds) (`CAPTYPE cap, Bool isTopIncluded) = isInBounds(capArg(cap), isTopIncluded);
|
||||||
|
(* noinline *)
|
||||||
|
function Exact#(`CAPTYPE) `W(setBounds) (`CAPTYPE cap, Bit#(CapAddrW) length) = capExactRet(setBounds(capArg(cap), length));
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function `CAPTYPE `W(nullWithAddr) (Bit#(CapAddrW) addr) = nullWithAddr(addr);
|
function `CAPTYPE `W(nullWithAddr) (Bit#(CapAddrW) addr) = nullWithAddr(addr);
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
@@ -95,5 +107,15 @@ function Bool `W(validAsType) (`CAPTYPE dummy, Bit#(CapAddrW) checkType) = valid
|
|||||||
function `CAPTYPE `W(fromMem) (Tuple2#(Bool, Bit#(CapW)) mem_cap) = fromMem(mem_cap);
|
function `CAPTYPE `W(fromMem) (Tuple2#(Bool, Bit#(CapW)) mem_cap) = fromMem(mem_cap);
|
||||||
(* noinline *)
|
(* noinline *)
|
||||||
function Tuple2#(Bool, Bit#(CapW)) `W(toMem) (`CAPTYPE cap) = toMem(cap);
|
function Tuple2#(Bool, Bit#(CapW)) `W(toMem) (`CAPTYPE cap) = toMem(cap);
|
||||||
|
(* noinline *)
|
||||||
|
function Bit#(CapAddrW) `W(getRepresentableAlignmentMask) (`CAPTYPE dummy, Bit#(CapAddrW) length) = alignmentMask (capArg(dummy), length);
|
||||||
|
(* noinline *)
|
||||||
|
function Bit#(CapAddrW) `W(getRepresentableLength) (`CAPTYPE dummy, Bit#(CapAddrW) length) = roundLength (capArg(dummy), length);
|
||||||
|
(* noinline *)
|
||||||
|
function Bit#(2) `W(getBaseAlignment) (`CAPTYPE cap) = getBaseAlignment(capArg(cap));
|
||||||
|
(* noinline *)
|
||||||
|
function Bool `W(isDerivable) (`CAPTYPE cap) = isDerivable(capArg(cap));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
|||||||
261
CHERICapWrap.py
Executable file
261
CHERICapWrap.py
Executable file
@@ -0,0 +1,261 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description=
|
||||||
|
'''Generates a Blarney wrapper for the given Bluespec generated verilog file
|
||||||
|
containing a module definition of a purely combinational CHERI function.
|
||||||
|
''')
|
||||||
|
parser.add_argument('verilog_files', metavar='VERILOG_FILE', type=str, nargs='+',
|
||||||
|
help='The file(s) to process')
|
||||||
|
parser.add_argument('--output', '-o', metavar='OUTPUT_FILE', type=str, nargs='?',
|
||||||
|
default="",
|
||||||
|
help='The output Blarney Haskell module to generate')
|
||||||
|
parser.add_argument('--generator', metavar='GENERATOR', type=str, nargs='?',
|
||||||
|
choices=['blarney','sv','systemverilog'], default='blarney',
|
||||||
|
help='The generator to be used')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Generic wrapper for a Verilog module
|
||||||
|
class Wrapper:
|
||||||
|
def __init__(self, size, name, ins, out):
|
||||||
|
self.size = size
|
||||||
|
self.name = name
|
||||||
|
self.ins = ins
|
||||||
|
self.out = out
|
||||||
|
def verilogModuleName(self):
|
||||||
|
return "module_wrap{:d}_{:s}".format(self.size, self.name)
|
||||||
|
def verilogInputNames(self):
|
||||||
|
return ["wrap{:d}_{:s}_{:s}".format(self.size, self.name, nm)
|
||||||
|
for nm in [x[0] for x in self.ins]]
|
||||||
|
def verilogOutputName(self):
|
||||||
|
return "wrap{:d}_{:s}".format(self.size, self.name)
|
||||||
|
|
||||||
|
|
||||||
|
# Generic generator class
|
||||||
|
# Describes the minimum functionality that a generator needs to implement.
|
||||||
|
# A generator takes some list of Verilog modules (which includes information
|
||||||
|
# about the module name, inputs, outputs, etc) and generates a list of file
|
||||||
|
# contents that should be written.
|
||||||
|
class Generator(ABC):
|
||||||
|
# namehint is a hint for naming, and each specific generator subclass will
|
||||||
|
# interpret it in its own way. In many cases it may be the single generated
|
||||||
|
# filename
|
||||||
|
def __init__(self, namehint, mods = None):
|
||||||
|
self.namehint = namehint
|
||||||
|
if mods is not None:
|
||||||
|
self.modules = mods
|
||||||
|
else:
|
||||||
|
self.modules = list()
|
||||||
|
|
||||||
|
def addVerilogModule(self, mod):
|
||||||
|
self.modules.append(mod)
|
||||||
|
|
||||||
|
# Generates a list of tuples containing output file names and output file
|
||||||
|
# contents to be written to disk
|
||||||
|
@abstractmethod
|
||||||
|
def emit(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Generates Blarney files
|
||||||
|
# When the namehint is not empty, it is used as the filename and .hs is appended
|
||||||
|
# otherwise the old default filename of CHERIBlarneyWrappers.hs is used
|
||||||
|
class BlarneyGenerator(Generator):
|
||||||
|
def emit(self):
|
||||||
|
modname = "CHERIBlarneyWrappers"
|
||||||
|
filename = modname + ".hs"
|
||||||
|
if self.namehint is not None and self.namehint != "":
|
||||||
|
modname = self.namehint
|
||||||
|
filename = self.namehint + ".hs"
|
||||||
|
|
||||||
|
contents = "module " + modname + " where\n\n"
|
||||||
|
contents += "import Blarney\n"
|
||||||
|
contents += "import Blarney.Core.BV\n"
|
||||||
|
for mod in self.modules:
|
||||||
|
print(mod.name)
|
||||||
|
contents += "\n"
|
||||||
|
ins_names = [x[0] for x in mod.ins]
|
||||||
|
ins_wdths = [x[1] for x in mod.ins]
|
||||||
|
str_type = "{:s} :: {:s}{:s}{:s}".format(
|
||||||
|
mod.name,
|
||||||
|
" -> ".join(["Bit {:d}".format(n) for n in ins_wdths]),
|
||||||
|
" -> " if mod.ins else "",
|
||||||
|
"Bit {:d}".format(mod.out[1]))
|
||||||
|
str_decl = "{:s} {:s} = FromBV $\n makePrim1 (Custom \"{:s}\" [{:s}] [{:s}] [] False Nothing) [{:s}]".format(
|
||||||
|
mod.name, " ".join(ins_names),
|
||||||
|
mod.verilogModuleName(),
|
||||||
|
", ".join(["(\"{:s}\", {:d})".format(n, w)
|
||||||
|
for (n, w) in zip(mod.verilogInputNames(),
|
||||||
|
ins_wdths)]),
|
||||||
|
"(\"{:s}\", {:d})".format(mod.verilogOutputName(), mod.out[1]),
|
||||||
|
", ".join(["toBV {:s}".format(nm) for nm in ins_names]))
|
||||||
|
contents += "{:s}\n{:s}".format(str_type, str_decl)
|
||||||
|
contents += "\n".format(str_decl)
|
||||||
|
|
||||||
|
|
||||||
|
return [(filename, contents)]
|
||||||
|
|
||||||
|
# generates SystemVerilog files
|
||||||
|
# when the namehint is non-empty it is used as a prefix for the file name
|
||||||
|
# generates a _pkg.sv file containing:
|
||||||
|
# a typedef of cheri_cap_t which is an "opaque" capability
|
||||||
|
# a typedef of cheri_cap_dec_t which is a decompressed capability
|
||||||
|
# generates a _mod.sv file containing a module which combinationally takes an "opaque"
|
||||||
|
# capability as the input and gives a decompressed capability as the output
|
||||||
|
# In the filename, "cheri" is appended with the in-memory capability width
|
||||||
|
# (i.e. the filename will be "cheri64_pkg.sv") so that differently sized capabilities
|
||||||
|
# can exist
|
||||||
|
class SystemVerilogGenerator(Generator):
|
||||||
|
def emit(self):
|
||||||
|
# get the size of in-memory capability by assuming that the name of the
|
||||||
|
# modules matches "module_wrap$SIZE_..." and taking just the size part
|
||||||
|
in_mem_cap_size = self.modules[0].verilogModuleName()[11:]
|
||||||
|
in_mem_cap_size = in_mem_cap_size[:in_mem_cap_size.find("_")]
|
||||||
|
|
||||||
|
cap_type_name = "cheri_cap_t" # the name of the opaque cap type
|
||||||
|
cap_dec_type_name = "cheri_cap_dec_t" # the name of the expanded cap type
|
||||||
|
cap_dec_mod_name = "cheri{:s}_cap_expander".format(in_mem_cap_size) # the name of the expanding module
|
||||||
|
cap_in_signal_name = "cap_i" # the name of the input signal to the expanding module
|
||||||
|
cap_out_signal_name = "cap_o" # the name of the output signal to the expanding module
|
||||||
|
cap_search_string = "cap" # the string required for inferring capability width
|
||||||
|
|
||||||
|
|
||||||
|
# this wrapper generator is intended to make access to the "getter"
|
||||||
|
# modules (i.e. getAddr, getTop, etc) easier/cleaner
|
||||||
|
# ideally, to access the field it is cleaner to say "cap.address" rather than
|
||||||
|
# "cap.getAddress" or "cap.validCap" rather than "cap.isValidCap"
|
||||||
|
# the following is a list of "keywords" to remove from the field names
|
||||||
|
keywords_to_remove = ["get", "is"]
|
||||||
|
def mod_name_to_field_name(modname):
|
||||||
|
for kw in keywords_to_remove:
|
||||||
|
if modname.startswith(kw):
|
||||||
|
# remove the keyword from the start
|
||||||
|
modname = modname[len(kw):]
|
||||||
|
# make first letter lowercase
|
||||||
|
modname = modname[0].lower() + modname[1:]
|
||||||
|
# return after removing the first found keyword
|
||||||
|
return modname
|
||||||
|
return modname
|
||||||
|
|
||||||
|
pkg_file_name = "cheri{:s}_pkg.sv".format(in_mem_cap_size)
|
||||||
|
module_file_name = "{:s}.sv".format(cap_dec_mod_name)
|
||||||
|
|
||||||
|
# prepend namehint if non-empty
|
||||||
|
if self.namehint is not None and self.namehint != "":
|
||||||
|
pkg_file_name = self.namehint + "_" + pkg_file_name
|
||||||
|
module_file_name = self.namehint + "_" + module_file_name
|
||||||
|
|
||||||
|
pkg_name = pkg_file_name[:-3]
|
||||||
|
dec_mod_name = module_file_name[:-3]
|
||||||
|
|
||||||
|
# find the size of a capability by assuming that any no-input modules
|
||||||
|
# with "cap" in the name have a capability output
|
||||||
|
cap_size = None
|
||||||
|
for mod in self.modules:
|
||||||
|
if len(mod.ins) != 0 or cap_search_string not in mod.name.lower():
|
||||||
|
continue
|
||||||
|
cap_size = mod.out[1]
|
||||||
|
break
|
||||||
|
|
||||||
|
if cap_size == None:
|
||||||
|
# the above method failed to find a capability size
|
||||||
|
# to fix, can either implement a better method or just hard-code the capability size
|
||||||
|
raise NotImplementedError("Unable to determine capability size from input files")
|
||||||
|
|
||||||
|
|
||||||
|
cap_type_def_text = " typedef logic [{:d}:0] {:s};\n".format(cap_size-1, cap_type_name)
|
||||||
|
|
||||||
|
# assume all modules with one capability-sized input are "getters"
|
||||||
|
# these will be the fields of the decompressed capability struct
|
||||||
|
struct_elems = list()
|
||||||
|
for mod in self.modules:
|
||||||
|
if len(mod.ins) == 1 and mod.ins[0][1] == cap_size:
|
||||||
|
struct_elems.append(mod)
|
||||||
|
|
||||||
|
# structure definition
|
||||||
|
struct_def_text = " typedef struct packed {\n"
|
||||||
|
for mod in struct_elems:
|
||||||
|
struct_def_text += " logic [{:d}:{:d}] {:s};\n".format(mod.out[1]-1, 0, mod_name_to_field_name(mod.name))
|
||||||
|
struct_def_text += " }} {:s};\n".format(cap_dec_type_name)
|
||||||
|
|
||||||
|
# package definition
|
||||||
|
pkg_def_text = "package {:s};\n".format(pkg_name)
|
||||||
|
pkg_def_text += cap_type_def_text
|
||||||
|
pkg_def_text += struct_def_text
|
||||||
|
pkg_def_text += "endpackage\n"
|
||||||
|
|
||||||
|
# module definition
|
||||||
|
module_def_text = "module {:s} (\n".format(cap_dec_mod_name)
|
||||||
|
module_def_text += " input {:s}::{:s} {:s},\n".format(pkg_name, cap_type_name, cap_in_signal_name)
|
||||||
|
module_def_text += " output {:s}::{:s} {:s}\n".format(pkg_name, cap_dec_type_name, cap_out_signal_name)
|
||||||
|
module_def_text += ");\n"
|
||||||
|
module_def_text += " import {:s}::*;\n".format(pkg_name)
|
||||||
|
|
||||||
|
# module instantiations
|
||||||
|
for mod in struct_elems:
|
||||||
|
module_def_text += " {:s} {:s}_mod (\n".format(mod.verilogModuleName(), mod.name)
|
||||||
|
module_def_text += " .{:s}({:s}),\n".format(mod.verilogInputNames()[0], cap_in_signal_name)
|
||||||
|
module_def_text += " .{:s}({:s}.{:s})\n".format(mod.verilogOutputName(), cap_out_signal_name, mod_name_to_field_name(mod.name))
|
||||||
|
module_def_text += " );\n"
|
||||||
|
|
||||||
|
module_def_text += "endmodule\n"
|
||||||
|
|
||||||
|
return [(pkg_file_name, pkg_def_text),
|
||||||
|
(module_file_name, module_def_text)]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# define module regexp
|
||||||
|
modDecl = re.compile("^module\s+module_wrap(\d+)_(\w+)\(")
|
||||||
|
# TODO handle size 1
|
||||||
|
#
|
||||||
|
# gather the list of modules
|
||||||
|
wrappers = []
|
||||||
|
for fname in args.verilog_files:
|
||||||
|
size = 0
|
||||||
|
name = None
|
||||||
|
ins = []
|
||||||
|
out = ("",0)
|
||||||
|
with open(fname, "r") as f:
|
||||||
|
for ln in f:
|
||||||
|
modM = modDecl.match(ln)
|
||||||
|
if modM:
|
||||||
|
size = int(modM.group(1))
|
||||||
|
name = modM.group(2)
|
||||||
|
break
|
||||||
|
if not name:
|
||||||
|
print("Couldn't find a valid Verilog module definition")
|
||||||
|
exit(-1)
|
||||||
|
# define input/output regexp
|
||||||
|
inDecl = re.compile("^\s*input(\s+\[(\d+)\s+:\s+0\])?\s+wrap(\d+)_"+name+"_(\w+);")
|
||||||
|
outDecl = re.compile("^\s*output(\s+\[(\d+)\s+:\s+0\])?\s+wrap(\d+)_"+name+";")
|
||||||
|
|
||||||
|
for ln in f:
|
||||||
|
inM = inDecl.match(ln)
|
||||||
|
outM = outDecl.match(ln)
|
||||||
|
if inM:
|
||||||
|
ins.append((inM.group(4), (int(inM.group(2)) + 1) if inM.group(1) else 1))
|
||||||
|
elif outM:
|
||||||
|
out = (name, (int(outM.group(2)) + 1) if outM.group(1) else 1)
|
||||||
|
#else:
|
||||||
|
# print("===>> no match for line: {:s}".format(ln))
|
||||||
|
wrappers.append(Wrapper(size, name, ins, out))
|
||||||
|
|
||||||
|
# choose the right generator based on the input argument
|
||||||
|
gen = None
|
||||||
|
if args.generator.lower() in ["systemverilog", "sv"]:
|
||||||
|
gen = SystemVerilogGenerator(args.output, wrappers)
|
||||||
|
elif args.generator.lower() in ["blarney"]:
|
||||||
|
gen = BlarneyGenerator(args.output, wrappers)
|
||||||
|
else:
|
||||||
|
print("Invalid generator selected; exiting")
|
||||||
|
return
|
||||||
|
|
||||||
|
for out in gen.emit():
|
||||||
|
with open(out[0], "w") as f:
|
||||||
|
f.write(out[1])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#! /usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=
|
|
||||||
'''Generates a Blarney wrapper for the given Bluespec generated verilog file
|
|
||||||
containing a module definition of a purely combinational CHERI function.
|
|
||||||
''')
|
|
||||||
parser.add_argument('verilog_files', metavar='VERILOG_FILE', type=str, nargs='+',
|
|
||||||
help='The file(s) to process')
|
|
||||||
parser.add_argument('--output', '-o', metavar='OUTPUT_FILE', type=str, nargs='?',
|
|
||||||
default="CHERIBlarneyWrappers",
|
|
||||||
help='The output Blarney Haskell module to generate')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
class BlarneyWrapper:
|
|
||||||
def __init__(self, size, name, ins, out):
|
|
||||||
self.size = size
|
|
||||||
self.name = name
|
|
||||||
self.ins = ins
|
|
||||||
self.out = out
|
|
||||||
def verilogModuleName(self):
|
|
||||||
return "module_wrap{:d}_{:s}".format(self.size, self.name)
|
|
||||||
def verilogInputNames(self):
|
|
||||||
return ["wrap{:d}_{:s}_{:s}".format(self.size, self.name, nm)
|
|
||||||
for nm in [x[0] for x in self.ins]]
|
|
||||||
def verilogOutputName(self):
|
|
||||||
return "wrap{:d}_{:s}".format(self.size, self.name)
|
|
||||||
def emitBlarney(self):
|
|
||||||
ins_names = [x[0] for x in self.ins]
|
|
||||||
ins_wdths = [x[1] for x in self.ins]
|
|
||||||
str_type = "{:s} :: {:s}{:s}{:s}".format(
|
|
||||||
self.name,
|
|
||||||
" -> ".join(["Bit {:d}".format(n) for n in ins_wdths]),
|
|
||||||
" -> " if self.ins else "",
|
|
||||||
"Bit {:d}".format(self.out[1]))
|
|
||||||
str_decl = "{:s} {:s} = FromBV $\n makePrim1 (Custom \"{:s}\" [{:s}] [{:s}] [] False Nothing) [{:s}]".format(
|
|
||||||
self.name, " ".join(ins_names),
|
|
||||||
self.verilogModuleName(),
|
|
||||||
", ".join(["(\"{:s}\", {:d})".format(n, w)
|
|
||||||
for (n, w) in zip(self.verilogInputNames(),
|
|
||||||
ins_wdths)]),
|
|
||||||
"(\"{:s}\", {:d})".format(self.verilogOutputName(), self.out[1]),
|
|
||||||
", ".join(["toBV {:s}".format(nm) for nm in ins_names]))
|
|
||||||
return "{:s}\n{:s}".format(str_type, str_decl)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# define module regexp
|
|
||||||
modDecl = re.compile("^module\s+module_wrap(\d+)_(\w+)\(")
|
|
||||||
# TODO handle size 1
|
|
||||||
#
|
|
||||||
wrappers = []
|
|
||||||
for fname in args.verilog_files:
|
|
||||||
size = 0
|
|
||||||
name = None
|
|
||||||
ins = []
|
|
||||||
out = ("",0)
|
|
||||||
with open(fname, "r") as f:
|
|
||||||
for ln in f:
|
|
||||||
modM = modDecl.match(ln)
|
|
||||||
if modM:
|
|
||||||
size = int(modM.group(1))
|
|
||||||
name = modM.group(2)
|
|
||||||
break
|
|
||||||
if not name:
|
|
||||||
print("Couldn't find a valid Verilog module definition")
|
|
||||||
exit(-1)
|
|
||||||
# define input/output regexp
|
|
||||||
inDecl = re.compile("^\s*input(\s+\[(\d+)\s+:\s+0\])?\s+wrap(\d+)_"+name+"_(\w+);")
|
|
||||||
outDecl = re.compile("^\s*output(\s+\[(\d+)\s+:\s+0\])?\s+wrap(\d+)_"+name+";")
|
|
||||||
|
|
||||||
for ln in f:
|
|
||||||
inM = inDecl.match(ln)
|
|
||||||
outM = outDecl.match(ln)
|
|
||||||
if inM:
|
|
||||||
ins.append((inM.group(4), (int(inM.group(2)) + 1) if inM.group(1) else 1))
|
|
||||||
elif outM:
|
|
||||||
out = (name, (int(outM.group(2)) + 1) if outM.group(1) else 1)
|
|
||||||
#else:
|
|
||||||
# print("===>> no match for line: {:s}".format(ln))
|
|
||||||
wrappers.append(BlarneyWrapper(size, name, ins, out))
|
|
||||||
|
|
||||||
with open(args.output+".hs", "w") as f:
|
|
||||||
#print("module CHERI{:d} where\n".format(size))
|
|
||||||
f.write("module "+args.output+" where\n\n")
|
|
||||||
f.write("import Blarney\n")
|
|
||||||
f.write("import Blarney.Core.BV\n")
|
|
||||||
for w in wrappers:
|
|
||||||
f.write("\n{:s}\n".format(w.emitBlarney()))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
4
Makefile
4
Makefile
@@ -45,5 +45,5 @@ clean-counterexamples:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
full-clean: clean clean-counterexamples
|
full-clean: clean clean-counterexamples
|
||||||
|
|||||||
@@ -180,4 +180,3 @@ module assert_prop_setBounds(
|
|||||||
always @(*) begin
|
always @(*) begin
|
||||||
assert(prop_ok);
|
assert(prop_ok);
|
||||||
end
|
end
|
||||||
endmodule
|
|
||||||
@@ -51,4 +51,3 @@ counterexamples/module_prop_getLength.v
|
|||||||
counterexamples/module_prop_isInBounds.v
|
counterexamples/module_prop_isInBounds.v
|
||||||
counterexamples/module_prop_setAddr.v
|
counterexamples/module_prop_setAddr.v
|
||||||
counterexamples/module_prop_fromToMem.v
|
counterexamples/module_prop_fromToMem.v
|
||||||
counterexamples/module_prop_setBounds.v
|
|
||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1738579205,
|
||||||
|
"narHash": "sha256-o6BeeanSUALvz8oL2CHOikVjCf7j+HqlA0WGvKOUX3Q=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "be5cf18b3d26ba2db938a72ade93ac8a9a7462ff",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "release-24.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
25
flake.nix
Normal file
25
flake.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/release-24.11";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let # helper bindings
|
||||||
|
# imported nix packages
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
# shell environment
|
||||||
|
dfltShell = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
sby
|
||||||
|
boolector
|
||||||
|
haskellPackages.sv2v
|
||||||
|
bluespec
|
||||||
|
];
|
||||||
|
};
|
||||||
|
# output attribute set
|
||||||
|
in {
|
||||||
|
devShells.default = dfltShell;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
56
fusesoc/cheri-cap-lib-test.core
Normal file
56
fusesoc/cheri-cap-lib-test.core
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
CAPI=2:
|
||||||
|
name: "ucam:cheri:cheri-cap-lib-test"
|
||||||
|
description: "Targets to test cheri-cap-lib generator"
|
||||||
|
|
||||||
|
# different parameterizations of the generator, one is 64bit and the other is 128bit
|
||||||
|
generate:
|
||||||
|
cheri-cap-lib-64:
|
||||||
|
generator: cheri-cap-lib-gen
|
||||||
|
parameters:
|
||||||
|
capwidth : "CAP64"
|
||||||
|
|
||||||
|
cheri-cap-lib-128:
|
||||||
|
generator: cheri-cap-lib-gen
|
||||||
|
parameters:
|
||||||
|
capwidth : "CAP128"
|
||||||
|
|
||||||
|
# cores need to explicitly depend on the generator, so an empty fileset with
|
||||||
|
# a dependency on the generator's core is needed, like so:
|
||||||
|
filesets:
|
||||||
|
cheri-cap-lib-dep:
|
||||||
|
depend:
|
||||||
|
- ucam:cheri:cheri-cap-lib-verilog-generator
|
||||||
|
|
||||||
|
# the filelist can then be placed in the filesets section of the dependant core
|
||||||
|
# to add the dependency, so that fusesoc can find the generator
|
||||||
|
|
||||||
|
targets:
|
||||||
|
test64:
|
||||||
|
description: "Default target that generates 64bit versions"
|
||||||
|
default_tool: verilator
|
||||||
|
# this adds a dependency on the generator, so that it appears on the
|
||||||
|
# dependency tree
|
||||||
|
filesets:
|
||||||
|
- cheri-cap-lib-dep
|
||||||
|
# this invokes the 64bit parameterization of the generator which is
|
||||||
|
# declared above
|
||||||
|
generate:
|
||||||
|
- cheri-cap-lib-64
|
||||||
|
test128:
|
||||||
|
description: "Default target that generates 128bit versions"
|
||||||
|
default_tool: verilator
|
||||||
|
filesets:
|
||||||
|
- cheri-cap-lib-dep
|
||||||
|
generate:
|
||||||
|
- cheri-cap-lib-128
|
||||||
|
test:
|
||||||
|
description: "Default target that generates both 64bit and 128bit versions"
|
||||||
|
default_tool: verilator
|
||||||
|
filesets:
|
||||||
|
- cheri-cap-lib-dep
|
||||||
|
# this invokes both the 64bit and 128bit parameterizations of the generator
|
||||||
|
# which is declared above
|
||||||
|
generate:
|
||||||
|
- cheri-cap-lib-64
|
||||||
|
- cheri-cap-lib-128
|
||||||
|
|
||||||
15
fusesoc/cheri-cap-lib-verilog-generator.core
Normal file
15
fusesoc/cheri-cap-lib-verilog-generator.core
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
CAPI=2:
|
||||||
|
name: "ucam:cheri:cheri-cap-lib-verilog-generator"
|
||||||
|
description: "Generates Verilog versions of the cheri-cap-lib functions"
|
||||||
|
|
||||||
|
# the only parameter that this generator takes from the yaml file is
|
||||||
|
# capwidth, which will be defined as a macro during bluespec compilation
|
||||||
|
# the generated core file will have the name:
|
||||||
|
# "ucam:cheri:cheri-cap-lib-verilog-autogen-$WIDTH"
|
||||||
|
# where $WIDTH is the number after CAP in the capwidth parameter
|
||||||
|
generators:
|
||||||
|
cheri-cap-lib-gen:
|
||||||
|
interpreter: python3
|
||||||
|
command: fusesoc-script.py
|
||||||
|
# TODO caching? for now, no
|
||||||
|
|
||||||
124
fusesoc/fusesoc-script.py
Normal file
124
fusesoc/fusesoc-script.py
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import yaml
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
# get yaml file location from single argument
|
||||||
|
argparser = argparse.ArgumentParser()
|
||||||
|
argparser.add_argument('yaml_file')
|
||||||
|
|
||||||
|
args = argparser.parse_args()
|
||||||
|
|
||||||
|
# load yaml file
|
||||||
|
with open(args.yaml_file, "r") as yamlfile:
|
||||||
|
try:
|
||||||
|
print(yamlfile)
|
||||||
|
yamlcfg = yaml.safe_load(yamlfile)
|
||||||
|
except yaml.YAMLError as exc:
|
||||||
|
print(exc)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO debug
|
||||||
|
print(yamlcfg)
|
||||||
|
print(yamlcfg['parameters']['capwidth'])
|
||||||
|
|
||||||
|
# use capwidth if provided, or default to CAP64
|
||||||
|
cfgwidth = yamlcfg["parameters"]["capwidth"]
|
||||||
|
capwidth = "CAP64" if cfgwidth in [None, ""] else cfgwidth
|
||||||
|
|
||||||
|
# TODO debug
|
||||||
|
print("capwidth:")
|
||||||
|
print(capwidth)
|
||||||
|
|
||||||
|
# get current working directory for file generation
|
||||||
|
workdir = os.getcwd()
|
||||||
|
|
||||||
|
# location of source files is actually one level above the location of the core file
|
||||||
|
bsv_src_root = os.path.join(os.path.dirname(__file__), "..")
|
||||||
|
print("bsv_src_root")
|
||||||
|
print(bsv_src_root)
|
||||||
|
|
||||||
|
# set up bluespec command + arguments
|
||||||
|
bscargs = list()
|
||||||
|
bscargs.extend(["bsc"])
|
||||||
|
bscargs.extend(["-vdir", workdir])
|
||||||
|
bscargs.extend(["-bdir", workdir])
|
||||||
|
bscargs.extend(["-simdir", workdir])
|
||||||
|
bscargs.extend(["-D", "{}".format(capwidth)])
|
||||||
|
bscargs.extend(["-D", "RISCV"])
|
||||||
|
bscargs.extend(["-verilog"])
|
||||||
|
bscargs.extend(["-u"])
|
||||||
|
bscargs.extend(["CHERICapWrap.bsv"])
|
||||||
|
# TODO debug
|
||||||
|
print(bscargs)
|
||||||
|
|
||||||
|
# blocking call to bsc to generate verilog files
|
||||||
|
subprocess.call(bscargs, cwd=bsv_src_root)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print("CCL AUTOGEN NAME:")
|
||||||
|
print("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:]))
|
||||||
|
|
||||||
|
|
||||||
|
# collect verilog files
|
||||||
|
vfiles = glob.glob(os.path.join(os.getcwd(), "*.v"))
|
||||||
|
print("verilog files:")
|
||||||
|
print(vfiles)
|
||||||
|
|
||||||
|
# generate systemverilog files
|
||||||
|
subprocess.call(["python3", bsv_src_root + "/CHERICapWrap.py", "--generator", "sv"] + vfiles, cwd=workdir)
|
||||||
|
|
||||||
|
# collect systemverilog files and package files
|
||||||
|
pkgfiles = glob.glob(os.path.join(os.getcwd(), "*_pkg.sv"))
|
||||||
|
svfiles = glob.glob(os.path.join(os.getcwd(), "*.sv"))
|
||||||
|
#
|
||||||
|
# remove package files from systemverilog files
|
||||||
|
for pkgfile in pkgfiles:
|
||||||
|
svfiles.remove(pkgfile)
|
||||||
|
|
||||||
|
print("svfiles and pkgfiles:")
|
||||||
|
print(svfiles)
|
||||||
|
print(pkgfiles)
|
||||||
|
|
||||||
|
# write core file
|
||||||
|
with open("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:]), 'w') as f:
|
||||||
|
txt = 'CAPI=2:\n'
|
||||||
|
txt += 'name: "ucam:cheri:cheri-cap-lib-verilog-autogen-{}"\n'.format(capwidth[3:])
|
||||||
|
txt += 'description: "Autogenerated Verilog & SystemVerilog versions of the cheri-cap-lib functions"\n\n'
|
||||||
|
|
||||||
|
txt += 'filesets:\n'
|
||||||
|
txt += ' files_pkg:\n'
|
||||||
|
txt += ' files:\n'
|
||||||
|
txt += ' - '
|
||||||
|
txt += '\n - '.join([os.path.basename(file) for file in pkgfiles])
|
||||||
|
txt += '\n'
|
||||||
|
txt += ' file_type: systemVerilogSource\n'
|
||||||
|
txt += ' files_v:\n'
|
||||||
|
txt += ' files:\n'
|
||||||
|
txt += ' - '
|
||||||
|
txt += '\n - '.join([os.path.basename(file) for file in vfiles])
|
||||||
|
txt += '\n'
|
||||||
|
txt += ' file_type: verilogSource\n'
|
||||||
|
txt += ' files_sv:\n'
|
||||||
|
txt += ' files:\n'
|
||||||
|
txt += ' - '
|
||||||
|
txt += '\n - '.join([os.path.basename(file) for file in svfiles])
|
||||||
|
txt += '\n'
|
||||||
|
txt += ' file_type: systemVerilogSource\n'
|
||||||
|
txt += '\n'
|
||||||
|
|
||||||
|
txt += 'targets:\n'
|
||||||
|
txt += ' default:\n'
|
||||||
|
txt += ' description: "Default target that contains the Verilog files"\n'
|
||||||
|
txt += ' filesets:\n'
|
||||||
|
txt += ' - files_v\n'
|
||||||
|
txt += ' - files_pkg\n'
|
||||||
|
txt += ' - files_sv\n'
|
||||||
|
|
||||||
|
f.write(txt)
|
||||||
|
|
||||||
|
|
||||||
38
readme.adoc
38
readme.adoc
@@ -48,3 +48,41 @@ toc::[]
|
|||||||
== The CHERI CAP LIB API
|
== The CHERI CAP LIB API
|
||||||
|
|
||||||
include::CHERI_CAP_API.adoc[]
|
include::CHERI_CAP_API.adoc[]
|
||||||
|
|
||||||
|
== FuseSoC
|
||||||
|
|
||||||
|
``cheri-cap-lib`` supports being used as a generator within FuseSoC. The scripts
|
||||||
|
and cores for this are within the ``fusesoc`` directory, and the file
|
||||||
|
``cheri-cap-lib-test.core`` shows how a core can declare a dependency on and
|
||||||
|
use the ``cheri-cap-lib`` generator.
|
||||||
|
|
||||||
|
The FuseSoC generator will generate Verilog modules for each of the functions
|
||||||
|
in ``CHERICapWrap.bsv``, as well as a SystemVerilog package and module to
|
||||||
|
make working with these modules _slightly_ simpler in SystemVerilog.
|
||||||
|
|
||||||
|
To generate both the 64bit and 128bit wrappers and packages, run
|
||||||
|
``fusesoc --cores-root . run --target test --setup ucam:cheri:cheri-cap-lib-test``
|
||||||
|
|
||||||
|
The files will be generated in
|
||||||
|
``build/ucam_cheri_cheri-cap-lib-test_0/src/ucam_cheri_cheri-cap-lib-verilog-autogen-64_0/``
|
||||||
|
and
|
||||||
|
``build/ucam_cheri_cheri-cap-lib-test_0/src/ucam_cheri_cheri-cap-lib-verilog-autogen-128_0/``
|
||||||
|
|
||||||
|
These fusesoc cores and instructions work using fusesoc 0.3 as installed
|
||||||
|
by running
|
||||||
|
``pip3 install -r python-requirements.txt``
|
||||||
|
within the OpenTitan GitHub repository as of commit
|
||||||
|
https://github.com/lowRISC/opentitan/commit/2438b17afe4fef0d815be2e890763c288c449918
|
||||||
|
|
||||||
|
== Formal Verification
|
||||||
|
|
||||||
|
A small number of properties have been specified in `CHERICapProp.bsv`.
|
||||||
|
These can be verfied using SymbiYosys, e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-07-17/oss-cad-suite-linux-x64-20240717.tgz
|
||||||
|
tar xzf oss-cad-suite-linux-x64-20240717.tgz
|
||||||
|
export PATH=`pwd`/oss-cad-suite/bin:$PATH
|
||||||
|
make verilog-props
|
||||||
|
sby -f check.sby
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user