Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add more coverage target #837

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ $ nix develop -c t1-helper run -i <top-name> -c <config-name> -e <emulator-type>
wheres
- `<config-name>` is the configuration name
- `<top-name>` is one of the `t1emu`, `t1rocketemu`
- `<emulator-type>` is one of the `verilator-emu`, `verilator-emu-trace`, `vcs-emu`, `vcs-emu-trace`
- `<emulator-type>` is one of the `verilator-emu`, `verilator-emu-trace`, `vcs-emu`, `vcs-emu-trace`, `vcs-emu-cover`
- `<case-name>` is the name of a testcase, you can resolve runnable test cases by command: `t1-helper listCases -c <config-name> <regexp>`

For example:
Expand Down Expand Up @@ -206,6 +206,12 @@ $ nix develop -c t1-helper check
The `t1-helper check` subcommand will read RTL event produced in `run` stage,
so make sure you `run` a test before `check`.

To get the coverage report, use the `vcs-emu-cover` emulator type:

```console
$ nix develop -c t1-helper run -i t1emu -c blastoise -e vcs-emu-cover mlir.hello
```

#### Export RTL Properties

```shell
Expand Down
2 changes: 2 additions & 0 deletions nix/t1/conversion/sv-to-vcs-simulator.nix
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ stdenv.mkDerivation rec {
"assert"
"-cm_dir"
"./cm"
"-assert"
"enable_hier"
]
++ lib.optionals (!enableCover) [
"-assert"
Expand Down
2 changes: 2 additions & 0 deletions nix/t1/run/run-vcs-emu.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ stdenvNoCC.mkDerivation (rec {
++ lib.optionals emulator.enableCover [
"-cm"
"assert"
"-assert"
"hier=${testCase}/${testCase.pname}.cover"
]
++ lib.optionals emulator.enableTrace [
"+t1_wave_path=${testCase.pname}.fsdb"
Expand Down
38 changes: 31 additions & 7 deletions script/emu/src/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ object Main:
attr
) ++ extraArgs
os.proc(args).call().out.trim()

def resolveTestElfPath(
def resolveTestPath(
ip: String,
config: String,
caseName: String,
forceX86: Boolean = false
): os.Path =
): os.Path =
val casePath = os.Path(caseName, os.pwd)
if (os.exists(casePath)) then return casePath

Expand All @@ -61,11 +61,35 @@ object Main:
val nixStorePath = resolveNixPath(
s"${caseAttrRoot}t1.${config}.${ip}.cases.${caseName}"
)
val elfFilePath = os.Path(nixStorePath) / "bin" / s"${caseName}.elf"
val filePath = os.Path(nixStorePath)

filePath
end resolveTestPath

def resolveTestElfPath(
ip: String,
config: String,
caseName: String,
forceX86: Boolean = false
): os.Path =
val testPath = resolveTestPath(ip, config, caseName, forceX86)
val elfFilePath = testPath / "bin" / s"${caseName}.elf"

elfFilePath
end resolveTestElfPath

def resolveTestCoverPath(
ip: String,
config: String,
caseName: String,
forceX86: Boolean = false
): os.Path =
val testPath = resolveTestPath(ip, config, caseName, forceX86)
val coverFilePath = testPath / s"${caseName}.cover"

coverFilePath
end resolveTestCoverPath

def resolveTestBenchPath(
ip: String,
config: String,
Expand Down Expand Up @@ -203,8 +227,8 @@ object Main:
s"Using config=${BOLD}${finalConfig.get}${RESET} emulator=${BOLD}${finalEmuType.get}${RESET} case=${BOLD}$caseName${RESET}"
)

val caseElfPath =
resolveTestElfPath(finalIp.get, finalConfig.get, caseName, forceX86)
val caseElfPath = resolveTestElfPath(finalIp.get, finalConfig.get, caseName, forceX86)
val caseCoverPath = resolveTestCoverPath(finalIp.get, finalConfig.get, caseName, forceX86)
val outputPath = prepareOutputDir(outDir.getOrElse("t1-sim-result"))
val emulator = resolveTestBenchPath(finalIp.get, finalConfig.get, finalEmuType.get)

Expand All @@ -216,7 +240,7 @@ object Main:
)
++ optionals(timeout.isDefined, Seq(s"+t1_timeout=${timeout.getOrElse("unreachable")}"))
++ optionals(isTrace, Seq(s"+t1_wave_path=${outputPath / "wave.fsdb"}"))
++ optionals(isCover, Seq(s"-cm assert"))
++ optionals(isCover, Seq("-cm", "assert", "-assert", s"hier=${caseCoverPath}"))
++ optionals(!leftOverArguments.isEmpty, leftOverArguments)

if dryRun.value then return
Expand Down
252 changes: 141 additions & 111 deletions t1/src/T1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import chisel3.util.{
Mux1H,
OHToUInt,
Pipe,
PriorityEncoder,
RegEnable,
UIntToOH,
Valid,
Expand Down Expand Up @@ -978,118 +979,147 @@ class T1(val parameter: T1Parameter)
probeWire.retire.valid := io.retire.rd.valid
probeWire.retire.bits := io.retire.rd.bits.rdData
probeWire.idle := slots.map(_.state.idle).reduce(_ && _)
}

// coverage
// unsupported 64-bit instructions for 32-bit xlen
val zve32f = Seq(
// format: off
"vfadd.vf", "vfadd.vv", "vfclass.v", "vfcvt.f.x.v",
"vfcvt.f.xu.v", "vfcvt.rtz.x.f.v", "vfcvt.rtz.xu.f.v", "vfcvt.x.f.v",
"vfcvt.xu.f.v", "vfdiv.vf", "vfdiv.vv", "vfmacc.vf",
"vfmacc.vv", "vfmadd.vf", "vfmadd.vv", "vfmax.vf",
"vfmax.vv", "vfmerge.vfm", "vfmin.vf", "vfmin.vv",
"vfmsac.vf", "vfmsac.vv", "vfmsub.vf", "vfmsub.vv",
"vfmul.vf", "vfmul.vv", "vfmv.f.s", "vfmv.s.f",
"vfmv.v.f", "vfnmacc.vf", "vfnmacc.vv", "vfnmadd.vf",
"vfnmadd.vv", "vfnmsac.vf", "vfnmsac.vv", "vfnmsub.vf",
"vfnmsub.vv", "vfrdiv.vf", "vfrec7.v", "vfredmax.vs",
"vfredmin.vs", "vfredosum.vs", "vfredusum.vs", "vfrsqrt7.v",
"vfrsub.vf", "vfsgnj.vf", "vfsgnj.vv", "vfsgnjn.vf",
"vfsgnjn.vv", "vfsgnjx.vf", "vfsgnjx.vv", "vfsqrt.v",
"vfsub.vf", "vfsub.vv", "vmfeq.vf", "vmfeq.vv",
"vmfge.vf", "vmfgt.vf", "vmfle.vf", "vmfle.vv",
"vmflt.vf", "vmflt.vv", "vmfne.vf", "vmfne.vv"
// format: on
)
val zve64f = Seq(
// format: off
"vfncvt.f.f.w", "vfncvt.f.x.w", "vfncvt.f.xu.w", "vfncvt.rod.f.f.w", "vfncvt.rtz.x.f.w", "vfncvt.rtz.xu.f.w", "vfncvt.x.f.w", "vfncvt.xu.f.w",
"vfslide1down.vf", "vfslide1up.vf",
"vfwadd.vf", "vfwadd.vv", "vfwadd.wf", "vfwadd.wv",
"vfwcvt.f.f.v", "vfwcvt.f.x.v", "vfwcvt.f.xu.v", "vfwcvt.rtz.x.f.v", "vfwcvt.rtz.xu.f.v", "vfwcvt.x.f.v", "vfwcvt.xu.f.v",
"vfwmacc.vf", "vfwmacc.vv", "vfwmsac.vf", "vfwmsac.vv",
"vfwmul.vf", "vfwmul.vv", "vfwnmacc.vf", "vfwnmacc.vv",
"vfwnmsac.vf", "vfwnmsac.vv", "vfwredosum.vs", "vfwredusum.vs",
"vfwsub.vf", "vfwsub.vv", "vfwsub.wf", "vfwsub.wv",
// format: on
)
val zve64x = Seq(
// format: off
"vl1re64.v", "vl2re64.v", "vl4re64.v", "vl8re64.v",
"vle64.v", "vle64ff.v", "vloxei64.v", "vlse64.v", "vluxei64.v",
"vse64.v", "vsoxei64.v", "vsse64.v", "vsuxei64.v",
"vsext.vf8", "vzext.vf8"
// format: on
)
parameter.decoderParam.allInstructions.filter { instruction: Instruction =>
// format: off
!(zve64x.contains(instruction.name) && parameter.xLen == 32) &&
!(zve64f.contains(instruction.name) && parameter.xLen == 32 && parameter.fpuEnable) &&
!((zve32f ++ zve64f).contains(instruction.name) && !parameter.fpuEnable)
// format: on
}.map { instruction: Instruction =>
val issueMatch =
Sequence.BoolSequence(requestReg.bits.issue.instruction === BitPat("b" + instruction.encoding.toString))
CoverProperty(issueMatch, label = Some(s"t1_cover_issue_${instruction.name}"))
}

// new V Request from core
// val requestValidProbe: Bool = IO(Output(Probe(Bool())))
// val requestReadyProbe: Bool = IO(Output(Probe(Bool())))
// define(requestValidProbe, ProbeValue(request.valid))
// define(requestReadyProbe, ProbeValue(request.ready))

// Store decoded request
// val requestRegValidProbe: Bool = IO(Output(Probe(Bool())))
// define(requestRegValidProbe, ProbeValue(requestReg.valid))

/** Dispatch request from requestReg to lane
*
* There are four cases that might affect the ready status of requestRegDequeue:
* 1. executionReady: There are capable slot to load this instruction in top local 2. slotReady: Execution unit
* accept this instrution 3. !gatherNeedRead || gatherReadFinish: This is not a instrution which needs to wait
* for gather 4. instructionRAWReady: This is not instruction which will cause harzard that can not be avoid.
*/
// val requestRegDequeueValidProbe: Bool = IO(Output(Probe(Bool())))
// val requestRegDequeueReadyProbe: Bool = IO(Output(Probe(Bool())))
// define(requestRegDequeueValidProbe, ProbeValue(requestRegDequeue.valid))
// define(requestRegDequeueReadyProbe, ProbeValue(requestRegDequeue.ready))

// val executionReadyProbe = IO(Output(Probe(Bool())))
// define(executionReadyProbe, ProbeValue(executionReady))

// val slotReadyProbe = IO(Output(Probe(Bool())))
// define(slotReadyProbe, ProbeValue(slotReady))

// val gatherNeedReadProbe = IO(Output(Probe(Bool())))
// define(gatherNeedReadProbe, ProbeValue(gatherNeedRead))
// val gatherReadFinishProbe = IO(Output(Probe(Bool())))
// define(gatherReadFinishProbe, ProbeValue(gatherReadFinish))
// coverage
import Sequence.BoolSequence
// unsupported 64-bit instructions for 32-bit xlen
val zve32f = Seq(
// format: off
"vfadd.vf", "vfadd.vv", "vfclass.v", "vfcvt.f.x.v",
"vfcvt.f.xu.v", "vfcvt.rtz.x.f.v", "vfcvt.rtz.xu.f.v", "vfcvt.x.f.v",
"vfcvt.xu.f.v", "vfdiv.vf", "vfdiv.vv", "vfmacc.vf",
"vfmacc.vv", "vfmadd.vf", "vfmadd.vv", "vfmax.vf",
"vfmax.vv", "vfmerge.vfm", "vfmin.vf", "vfmin.vv",
"vfmsac.vf", "vfmsac.vv", "vfmsub.vf", "vfmsub.vv",
"vfmul.vf", "vfmul.vv", "vfmv.f.s", "vfmv.s.f",
"vfmv.v.f", "vfnmacc.vf", "vfnmacc.vv", "vfnmadd.vf",
"vfnmadd.vv", "vfnmsac.vf", "vfnmsac.vv", "vfnmsub.vf",
"vfnmsub.vv", "vfrdiv.vf", "vfrec7.v", "vfredmax.vs",
"vfredmin.vs", "vfredosum.vs", "vfredusum.vs", "vfrsqrt7.v",
"vfrsub.vf", "vfsgnj.vf", "vfsgnj.vv", "vfsgnjn.vf",
"vfsgnjn.vv", "vfsgnjx.vf", "vfsgnjx.vv", "vfsqrt.v",
"vfsub.vf", "vfsub.vv", "vmfeq.vf", "vmfeq.vv",
"vmfge.vf", "vmfgt.vf", "vmfle.vf", "vmfle.vv",
"vmflt.vf", "vmflt.vv", "vmfne.vf", "vmfne.vv"
// format: on
)
val zve64f = Seq(
// format: off
"vfncvt.f.f.w", "vfncvt.f.x.w", "vfncvt.f.xu.w", "vfncvt.rod.f.f.w", "vfncvt.rtz.x.f.w", "vfncvt.rtz.xu.f.w", "vfncvt.x.f.w", "vfncvt.xu.f.w",
"vfslide1down.vf", "vfslide1up.vf",
"vfwadd.vf", "vfwadd.vv", "vfwadd.wf", "vfwadd.wv",
"vfwcvt.f.f.v", "vfwcvt.f.x.v", "vfwcvt.f.xu.v", "vfwcvt.rtz.x.f.v", "vfwcvt.rtz.xu.f.v", "vfwcvt.x.f.v", "vfwcvt.xu.f.v",
"vfwmacc.vf", "vfwmacc.vv", "vfwmsac.vf", "vfwmsac.vv",
"vfwmul.vf", "vfwmul.vv", "vfwnmacc.vf", "vfwnmacc.vv",
"vfwnmsac.vf", "vfwnmsac.vv", "vfwredosum.vs", "vfwredusum.vs",
"vfwsub.vf", "vfwsub.vv", "vfwsub.wf", "vfwsub.wv",
// format: on
)
val zve64x = Seq(
// format: off
"vl1re64.v", "vl2re64.v", "vl4re64.v", "vl8re64.v",
"vle64.v", "vle64ff.v", "vloxei64.v", "vlse64.v", "vluxei64.v",
"vse64.v", "vsoxei64.v", "vsse64.v", "vsuxei64.v",
"vsext.vf8", "vzext.vf8"
// format: on
)
val instructions: Seq[Instruction] = parameter.decoderParam.allInstructions.filter { instruction: Instruction =>
// format: off
!(zve64x.contains(instruction.name) && parameter.xLen == 32) &&
!(zve64f.contains(instruction.name) && parameter.xLen == 32 && parameter.fpuEnable) &&
!((zve32f ++ zve64f).contains(instruction.name) && !parameter.fpuEnable)
// format: on
}

// val instructionRAWReadyProbe = IO(Output(Probe(Bool())))
// define(instructionRAWReadyProbe, ProbeValue(instructionRAWReady))
// End of requestRegDequeueProbe
// coverage for one instruction
instructions.map { instruction: Instruction =>
val coverMatch = BoolSequence(
requestReg.valid && requestReg.bits.issue.instruction === BitPat("b" + instruction.encoding.toString)
)
CoverProperty(coverMatch, label = Some(s"${instruction.name}"))
}

/** Response send back to core.
*
* There are four cases that might affect response is valid or not:
*
* 1. slot(n).state.sMaskUnit: The mask unit in slot n has finished its work. 2. slot(n).state.wLast: The execution
* unit in slot n has finished its work. 3. !slot(n).state.sCommit: This instruction doesn't committed. This is
* not an important signal so we don't capture it. 4. slot(n).record.instruction Index == responseCounter:
* current instruction is the oldest insn in V
*/
// val responseValidProbe: Bool = IO(Output(Probe(Bool())))
// define(responseValidProbe, ProbeValue(response.valid))

// val slotStateProbe: Seq[(Bool, Bool, Bool)] = slots.map { inst =>
// val sMaskUnitProbe = IO(Output(Probe(Bool())))
// define(sMaskUnitProbe, ProbeValue(inst.state.sMaskUnitExecution))
// val wLastProbe = IO(Output(Probe(Bool())))
// define(wLastProbe, ProbeValue(inst.state.wLast))
// val isLastInstProbe = IO(Output(Probe(Bool())))
// define(isLastInstProbe, ProbeValue(inst.record.instructionIndex === responseCounter))
// (sMaskUnitProbe, wLastProbe, isLastInstProbe)
// }
// // coverage for two instructions
// instructions.map { case instructionNew: Instruction =>
// instructions.map { case instructionOld: Instruction =>
// val issueInstructionOld = RegEnable(requestReg.bits.issue.instruction, requestReg.valid)
// val coverMatchNew = BoolSequence(
// requestReg.valid && requestReg.bits.issue.instruction === BitPat("b" + instructionNew.encoding.toString)
// )
// val coverMatchOld = BoolSequence(issueInstructionOld === BitPat("b" + instructionOld.encoding.toString))
// CoverProperty(
// coverMatchNew.and(coverMatchOld),
// label = Some(s"2_${instructionOld.name}_and_${instructionNew.name}")
// )
// }
// }

// // coverage for different sew / vlmul / vl
// val vsews: Seq[Int] = Seq(0, 1, 2)
// val sews: Seq[Int] = Seq(8, 16, 32)
// val vlmuls: Seq[Int] = Seq(0, 1, 2, 3, 6, 7)
// val lmuls: Seq[Double] = Seq(1, 2, 4, 8, -1, -1, 0.25, 0.5)
// val vls: Seq[(Double, Int)] =
// Seq((1.0, 0), (1.0, -1), (0.25, -1), (0.25, 0), (0.25, 1), (0.5, -1), (0.5, 0), (0.5, 1))
// vsews.map { vsew =>
// vlmuls.map { vlmul =>
// vls.map { case (vla, vlb) =>
// val sew = sews(vsew)
// val lmul = lmuls(vlmul)
// val vlmax = parameter.vLen * lmul / sew
// val vl = (vlmax * vla).toInt + vlb

// val coverMatch = BoolSequence(
// requestReg.valid && requestReg.bits.issue.vl === vl.U &&
// T1Issue.vlmul(requestReg.bits.issue) === vlmul.U &&
// T1Issue.vsew(requestReg.bits.issue) === vsew.U
// )

// CoverProperty(coverMatch, label = Some(s"3_sew_${sew}_lmul_${lmul}_vl_${vl}"))
// }
// }
// }

// // coverage for lsu (load / store / other) with slots (contain / intersection / disjoint)

// // TODO:load unit probe

// // store unit probe
// val storeUnitProbe = probeWire.lsuProbe.storeUnitProbe

// val storeRangeStartNew = storeUnitProbe.address
// val storeRangeEndNew = storeUnitProbe.address + PriorityEncoder(storeUnitProbe.mask)
// val storeRangeStartOld = RegEnable(storeUnitProbe.address, storeUnitProbe.valid)
// val storeRangeEndOld =
// RegEnable(storeUnitProbe.address + PriorityEncoder(storeUnitProbe.mask), storeUnitProbe.valid)

// val storeContain = storeRangeStartOld <= storeRangeStartNew && storeRangeEndNew <= storeRangeEndOld ||
// storeRangeStartNew <= storeRangeStartOld && storeRangeEndOld <= storeRangeEndNew
// val storeIntersection = storeRangeStartNew <= storeRangeStartOld && storeRangeEndNew <= storeRangeEndOld ||
// storeRangeStartOld <= storeRangeStartNew && storeRangeEndOld <= storeRangeEndNew
// val storeDisjoint = storeRangeEndNew <= storeRangeStartOld || storeRangeEndOld <= storeRangeEndNew

// CoverProperty(BoolSequence(storeUnitProbe.valid && storeContain), label = Some("4_store_contain"))
// CoverProperty(BoolSequence(storeUnitProbe.valid && storeIntersection), label = Some("4_store_intersection"))
// CoverProperty(BoolSequence(storeUnitProbe.valid && storeDisjoint), label = Some("4_store_disjoint"))

// // other unit probe
// val otherUnitProbe = probeWire.lsuProbe.otherUnitProbe

// val otherRangeStartNew = otherUnitProbe.address
// val otherRangeEndNew = otherUnitProbe.address + PriorityEncoder(otherUnitProbe.mask)
// val otherRangeStartOld = RegEnable(otherUnitProbe.address, otherUnitProbe.valid)
// val otherRangeEndOld =
// RegEnable(otherUnitProbe.address + PriorityEncoder(otherUnitProbe.mask), otherUnitProbe.valid)

// val otherContain = otherRangeStartOld <= otherRangeStartNew && otherRangeEndNew <= otherRangeEndOld ||
// otherRangeStartNew <= otherRangeStartOld && otherRangeEndOld <= otherRangeEndNew
// val otherIntersection = otherRangeStartNew <= otherRangeStartOld && otherRangeEndNew <= otherRangeEndOld ||
// otherRangeStartOld <= otherRangeStartNew && otherRangeEndOld <= otherRangeEndNew
// val otherDisjoint = otherRangeEndNew <= otherRangeStartOld || otherRangeEndOld <= otherRangeEndNew

// CoverProperty(BoolSequence(otherUnitProbe.valid && otherContain), label = Some("4_other_contain"))
// CoverProperty(BoolSequence(otherUnitProbe.valid && otherIntersection), label = Some("4_other_intersection"))
// CoverProperty(BoolSequence(otherUnitProbe.valid && otherDisjoint), label = Some("4_other_disjoint"))
} // end of verification layer
}
6 changes: 5 additions & 1 deletion tests/builder.nix
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ let

mkdir -p $out/bin
cp ${pname}.elf $out/bin
if [ -f ${pname}.cover ]; then
cp ${pname}.cover $out/
else
echo "-assert *" > $out/${pname}.cover
fi

${jqBin} --null-input \
--arg name ${pname} \
--arg type ${casePrefix} \
--arg elfPath "$out/bin/${pname}.elf" \
'{ "name": $name, "elf": { "path": $elfPath } }' \
> $out/${pname}.json
Expand Down
Loading