Compare commits

...

10 Commits

Author SHA1 Message Date
Jonathan Woodruff
1d2c0b953b Add a property that setBounds will only return a valid cap if the bounds are within the original bounds.
Some checks failed
check properties / tests (push) Has been cancelled
2025-03-07 15:12:46 +00:00
Peter Rugg
199e3c9f7f Extend forallCap to include almightyCap 2025-03-04 14:49:41 +00:00
Peter Rugg
7c9559524b Increase bit-widths of assertion modules
These were hardwired for RV64, but I'm assuming having too many
bits will not be a problem for RV32?
2025-03-04 14:49:41 +00:00
Peter Rugg
bad0d9cdb6 Default to 128-bit caps 2025-03-04 14:49:41 +00:00
Peter Rugg
c5da2ebc5d Move to building untracked files in subdirectories to improve cleaning 2025-03-04 14:49:41 +00:00
Alexandre Joannou
595447fd62 fix property following getLength return type change in b3896e4e50 2025-02-10 17:44:09 +00:00
Alexandre Joannou
2295c3b8c0 Added addAddrUnsafe verilog wrapper 2025-02-05 12:28:19 +00:00
Jonathan Woodruff
b3896e4e50 Move getLength to address length (not address length + 1), as we're explicitly saturating in order to express as a normal address. 2025-02-04 16:31:01 +00:00
Alexandre Joannou
3926b793bf Added an env var to control bsc vdir flag 2025-02-04 12:22:21 +00:00
Alexandre Joannou
9e0636e6fc Added test workflow 2025-02-04 11:10:09 +00:00
11 changed files with 222 additions and 62 deletions

16
.github/workflows/check-prop.yml vendored Normal file
View 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

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
build/
counterexamples/

View File

@@ -346,7 +346,7 @@ function BoundsInfo#(CapAddrW) getBoundsInfoFat (CapFat cap, TempFields tf)
Bit #(TAdd #(MW, 2)) correctTop = {pack (tf.topCorrection), topBits};
// Get the length by subtracting base from top and shifting appropriately, and
// saturate in case of big exponent
CapAddrPlus1 length =
CapAddr length =
(exp >= resetExp) ? ~0 : zeroExtend (correctTop - correctBase) << exp;
// compute repBase
@@ -437,12 +437,12 @@ function CapAddrPlus1 getTopFat(CapFat cap, TempFields tf);
ret[valueOf(CapAddrW)] = ~ret[valueOf(CapAddrW)];
return ret;
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
Bit#(TAdd#(MW,2)) top = {pack(tf.topCorrection),cap.bounds.topBits};
Bit#(TAdd#(MW,2)) base = {pack(tf.baseCorrection),cap.bounds.baseBits};
// 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
// TODO: The saturation behaviour here is short of being correct
return (cap.bounds.exp >= resetExp) ? ~0 : length;

View File

@@ -80,7 +80,7 @@ typedef union tagged {
typedef struct {
Bit #(addrW) base;
Bit #(TAdd #(addrW, 1)) top;
Bit #(TAdd #(addrW, 1)) length;
Bit #(addrW) length;
Bit #(addrW) repBase;
Bit #(TAdd #(addrW, 1)) repTop;
Bit #(TAdd #(addrW, 1)) repLength;
@@ -258,7 +258,7 @@ typeclass CHERICap #( type capT // type of the CHERICap capability
// Get the top
function Bit #(TAdd #(addrW, 1)) getTop (capT cap) = getBoundsInfo(cap).top;
// Get the length
function Bit #(TAdd #(addrW, 1)) getLength (capT cap) =
function Bit #(addrW) getLength (capT cap) =
getBoundsInfo(cap).length;
// Assertion that the capability's address is between its base and top
function Bool isInBounds (capT cap, Bool isTopIncluded) =

View File

@@ -49,12 +49,18 @@ function Bool implies(Bool x, Bool y) = !x || y;
function Bool forallBaseAndLen(CapAddr base, CapAddr len,
function Bool prop(CapPipe cap));
Exact#(CapPipe) baseCap = setAddr(almightyCap, base);
Exact#(CapPipe) boundedCap = setBounds(baseCap.value, len);
return baseCap.exact && implies
( boundedCap.exact
, prop(boundedCap.value)
);
Bool ret = ?;
if (base == 0 && ~len == 0) begin
ret = prop(almightyCap);
end else begin
Exact#(CapPipe) baseCap = setAddr(almightyCap, base);
Exact#(CapPipe) boundedCap = setBounds(baseCap.value, len);
ret = baseCap.exact && implies
( boundedCap.exact
, prop(boundedCap.value)
);
end
return ret;
endfunction
// Furthermore, every valid capability can be reached by calling setAddr on
@@ -107,10 +113,10 @@ function Bool prop_exact(CapAddr base, CapAddr len);
Exact#(CapPipe) baseCap = setAddr(almightyCap, base);
Exact#(CapPipe) boundedCap = setBounds(baseCap.value, len);
Exact#(CapPipe) baseCap2 = setAddr(almightyCap, getBase(boundedCap.value));
CapAddrPlus1 length = getLength(boundedCap.value);
Exact#(CapPipe) boundedCap2 = setBounds(baseCap2.value, truncate(length));
CapAddr length = getLength(boundedCap.value);
Exact#(CapPipe) boundedCap2 = setBounds(baseCap2.value, length);
return baseCap.exact && baseCap2.exact && implies
( truncateLSB(length) == 1'b0
( ~length != 0
, boundedCap2.exact
);
endfunction
@@ -137,7 +143,8 @@ endfunction
(* noinline *)
function Bool prop_getTop(CapAddr base, CapAddr len, CapAddr addr);
function prop(cap) = getTop(cap) == zeroExtend(base) + zeroExtend(len);
Bool reqAlmighty = ~len == 0;
function prop(cap) = getTop(cap) == zeroExtend(base) + (reqAlmighty ? {1'b1, 0} : zeroExtend(len));
return forallCap(base, len, addr, prop);
endfunction
@@ -185,4 +192,16 @@ function Bool prop_fromToMem(CapMem in);
return (cm == in);
endfunction
(* noinline *)
function Bool prop_setBounds(CapAddr base, CapAddr len, CapAddr addr, CapAddr new_len);
function prop(cap);
let new_cap = setBounds(cap,new_len).value;
return implies( isValidCap(new_cap),
getBase(cap) <= getBase(new_cap)
&& getTop(cap) >= getTop(new_cap)
);
endfunction
return forallCap(base, len, addr, prop);
endfunction
endpackage

View File

@@ -74,6 +74,8 @@ function Bit#(CapAddrW) `W(getAddr) (`CAPTYPE cap) = getAddr(cap);
(* noinline *)
function `CAPTYPE `W(setAddrUnsafe) (`CAPTYPE cap, Bit#(CapAddrW) addr) = capRet(setAddrUnsafe(capArg(cap), addr));
(* noinline *)
function `CAPTYPE `W(addAddrUnsafe) (`CAPTYPE cap, Bit#(TSub #(MW, 3)) inc) = capRet(addAddrUnsafe(capArg(cap), inc));
(* noinline *)
function Exact#(`CAPTYPE) `W(setAddr) (`CAPTYPE cap, Bit#(CapAddrW) addr) = capExactRet(setAddr(capArg(cap), addr));
(* noinline *)
function Bit#(CapAddrW) `W(getOffset) (`CAPTYPE cap) = getOffset(capArg(cap));
@@ -88,7 +90,7 @@ function Bit#(CapAddrW) `W(getBase) (`CAPTYPE cap) = getBase(capArg(cap));
(* noinline *)
function Bit#(TAdd#(CapAddrW, 1)) `W(getTop) (`CAPTYPE cap) = getTop(capArg(cap));
(* noinline *)
function Bit#(TAdd#(CapAddrW, 1)) `W(getLength) (`CAPTYPE cap) = getLength(capArg(cap));
function Bit#(CapAddrW) `W(getLength) (`CAPTYPE cap) = getLength(capArg(cap));
(* noinline *)
function Bool `W(isInBounds) (`CAPTYPE cap, Bool isTopIncluded) = isInBounds(capArg(cap), isTopIncluded);
(* noinline *)

View File

@@ -1,4 +1,4 @@
CAP ?= 64
CAP ?= 128
ifeq ($(CAP), 128)
BSCFLAGS = -D CAP128
else
@@ -13,27 +13,37 @@ ifeq ($(ARCH), RISCV)
BSCFLAGS += -D RISCV
endif
BSV_VERILOG_WRAPPERS_DIR ?= $(CURDIR)/build/
BUILD_DIR = $(BSV_VERILOG_WRAPPERS_DIR)
COUNTEREXAMPLE_DIR = $(CURDIR)/counterexamples/
BSCFLAGS += -bdir $(BUILD_DIR)
all: verilog-wrappers blarney-wrappers
verilog-wrappers: CHERICapWrap.bsv CHERICap.bsv CHERICC_Fat.bsv
bsc $(BSCFLAGS) -verilog -u $<
$(BUILD_DIR):
mkdir -p $@
verilog-props: CHERICapProps.bsv CHERICap.bsv CHERICC_Fat.bsv
bsc $(BSCFLAGS) -verilog -u $<
$(COUNTEREXAMPLE_DIR):
mkdir -p $@
check-prop: assertions.sv verilog-wrappers verilog-props
sby -f check.sby
verilog-wrappers: CHERICapWrap.bsv CHERICap.bsv CHERICC_Fat.bsv $(BUILD_DIR)
bsc $(BSCFLAGS) -vdir $(BSV_VERILOG_WRAPPERS_DIR) -verilog -u $<
blarney-wrappers: CHERICapWrap.py verilog-wrappers
./CHERICapWrap.py -o CHERIBlarneyWrappers *.v
verilog-props: CHERICapProps.bsv CHERICap.bsv CHERICC_Fat.bsv $(BUILD_DIR) $(COUNTEREXAMPLE_DIR)
bsc $(BSCFLAGS) -vdir $(COUNTEREXAMPLE_DIR) -verilog -u $<
.PHONY: clean clean-verilog-wrappers
check-prop: assertions.sv verilog-props $(COUNTEREXAMPLE_DIR)
sby --prefix $(COUNTEREXAMPLE_DIR) -f check.sby
clean-verilog-wrappers: clean
rm -f *.v
blarney-wrappers: CHERICapWrap.py verilog-wrappers $(BUILD_DIR)
./CHERICapWrap.py -o $(BUILD_DIR)/CHERIBlarneyWrappers $(BUILD_DIR)/*.v
clean-blarney-wrappers: clean
rm -f *.hs
.PHONY: clean clean-counterexamples full-clean
clean-counterexamples:
rm -rf $(COUNTEREXAMPLE_DIR)
clean:
rm -f *.bo
rm -rf $(BUILD_DIR)
full-clean: clean clean-counterexamples

View File

@@ -1,8 +1,8 @@
module assert_prop_unique(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len,
input wire [31 : 0] prop_newBase,
input wire [31 : 0] prop_newLen
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len,
input wire [63 : 0] prop_newBase,
input wire [63 : 0] prop_newLen
);
wire prop_ok;
@@ -20,8 +20,8 @@ module assert_prop_unique(
endmodule
module assert_prop_exact(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len
);
wire prop_ok;
@@ -37,8 +37,8 @@ module assert_prop_exact(
endmodule
module assert_prop_exactConditions(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len
);
wire prop_ok;
@@ -54,8 +54,8 @@ module assert_prop_exactConditions(
endmodule
module assert_prop_getBase(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len
);
wire prop_ok;
@@ -71,9 +71,9 @@ module assert_prop_getBase(
endmodule
module assert_prop_getTop(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len,
input wire [31 : 0] prop_addr
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len,
input wire [63 : 0] prop_addr
);
wire prop_ok;
@@ -90,9 +90,9 @@ module assert_prop_getTop(
endmodule
module assert_prop_getLength(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_addr,
input wire [31 : 0] prop_len
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_addr,
input wire [63 : 0] prop_len
);
wire prop_ok;
@@ -109,9 +109,9 @@ module assert_prop_getLength(
endmodule
module assert_prop_isInBounds(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len,
input wire [31 : 0] prop_addr
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len,
input wire [63 : 0] prop_addr
);
wire prop_ok;
@@ -128,9 +128,9 @@ module assert_prop_isInBounds(
endmodule
module assert_prop_setAddr(
input wire [31 : 0] prop_base,
input wire [31 : 0] prop_len,
input wire [31 : 0] prop_addr
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len,
input wire [63 : 0] prop_addr
);
wire prop_ok;
@@ -160,3 +160,24 @@ module assert_prop_fromToMem(
assert(prop_ok);
end
endmodule
module assert_prop_setBounds(
input wire [63 : 0] prop_base,
input wire [63 : 0] prop_len,
input wire [63 : 0] prop_addr,
input wire [63 : 0] prop_new_len
);
wire prop_ok;
module_prop_setBounds module_prop_setBounds_inst(
.prop_setBounds_base(prop_base),
.prop_setBounds_len(prop_len),
.prop_setBounds_addr(prop_addr),
.prop_setBounds_new_len(prop_new_len),
.prop_setBounds(prop_ok)
);
always @(*) begin
assert(prop_ok);
end
endmodule

View File

@@ -8,6 +8,7 @@ prop_getLength
prop_isInBounds
prop_setAddr
prop_fromToMem
prop_setBounds
[options]
depth 1
@@ -27,6 +28,7 @@ read -formal module_prop_getLength.v
read -formal module_prop_isInBounds.v
read -formal module_prop_setAddr.v
read -formal module_prop_fromToMem.v
read -formal module_prop_setBounds.v
prop_getBase: prep -top assert_prop_getBase
prop_getTop: prep -top assert_prop_getTop
prop_getLength: prep -top assert_prop_getLength
@@ -36,15 +38,17 @@ prop_exact: prep -top assert_prop_exact
prop_exactConditions: prep -top assert_prop_exactConditions
prop_setAddr: prep -top assert_prop_setAddr
prop_fromToMem: prep -top assert_prop_fromToMem
prop_setBounds: prep -top assert_prop_setBounds
[files]
assertions.sv
module_prop_unique.v
module_prop_exact.v
module_prop_exactConditions.v
module_prop_getBase.v
module_prop_getTop.v
module_prop_getLength.v
module_prop_isInBounds.v
module_prop_setAddr.v
module_prop_fromToMem.v
counterexamples/module_prop_unique.v
counterexamples/module_prop_exact.v
counterexamples/module_prop_exactConditions.v
counterexamples/module_prop_getBase.v
counterexamples/module_prop_getTop.v
counterexamples/module_prop_getLength.v
counterexamples/module_prop_isInBounds.v
counterexamples/module_prop_setAddr.v
counterexamples/module_prop_fromToMem.v
counterexamples/module_prop_setBounds.v

61
flake.lock generated
View 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
View 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;
}
);
}