Skip to content

Commit bf6b633

Browse files
author
MoritzScherer
authored
[ImportVerilog][Moore] Add support for %t format specifier, introduce moore.fmt.time (llvm#8979)
This PR adds support for using the %t format specifier in format strings by mapping them to the newly introduced `moore.fmt.time` / `moore::TimeFormatOp` in the ImportVerilog string formatter. According to the system verilog spec IEEE 1800-2023 § 20.4.3 "$timeformat", the exact formatting for any time value is dictated by the last $timeformat system task invoked within the scope or parent scopes of the format string, or alternatively, by a mix of constant default values and The smallest time precision argument of all the timescale compiler directives in the source description. Because of the formatting relying on context (who came up with this anyway? seems like a bad idea), and use of $timeformat is permitted in conditional settings, meaning it can't always be determined statically, I decided to not add a format attribute to moore::FormatTimeOp. I only added the width attribute, which may be defined in the formatter expression, e.g. %4t. Instead the semantics for implementing moore.fmt.time should rely on querying some global state that models the configuration set by the latest $timeformat system call within the scope. What's not fully clear to me is whether the System Verilog spec allows non-"time typed" values to be formatted with %t; I didn't find an explicit description of allowed values, but all examples in the spec I could find explicitly use the return value of a time-typed system task (e.g. $time) or a time-type parameter (e.g. parameter time _time = 100ns;). While it seems reasonable to suppose that also integer values are allowed (per the spec $time returns a 64-bit integer representing time), some examples in the doc also allow the use of $realtime (which returns a real value) with %t, so potentially any numeric value is allowed? In either case I didn't add this in the current implementation and instead only allow TimeType values, which in the context of Moore looked like the sanest option to me. Maybe someone has a clearer view on this.
1 parent 6e3b6ad commit bf6b633

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

include/circt/Dialect/Moore/MooreOps.td

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,27 @@ def FormatRealOp : MooreOp<"fmt.real", [Pure]> {
17341734
}];
17351735
}
17361736

1737+
def FormatTimeOp : MooreOp<"fmt.time", [Pure]> {
1738+
let summary = "Format a time value";
1739+
let description = [{
1740+
Format a time value as a string according to the format specified by the most recent
1741+
invocation of `$timeformat`.
1742+
1743+
See IEEE 1800-2023 § 21.2.1.1 "Format specifications" and
1744+
IEEE 1800-2023 § 20.4.3 "$timeformat".
1745+
}];
1746+
let arguments = (ins
1747+
TimeType:$value,
1748+
OptionalAttr<I32Attr>:$width
1749+
);
1750+
let results = (outs FormatStringType:$result);
1751+
let assemblyFormat = [{
1752+
$value
1753+
(`,` `width` $width^)?
1754+
attr-dict
1755+
}];
1756+
}
1757+
17371758
//===----------------------------------------------------------------------===//
17381759
// Builtin System Tasks and Functions
17391760
//===----------------------------------------------------------------------===//

lib/Conversion/ImportVerilog/FormatStrings.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ struct FormatStringParser {
135135
case 'f':
136136
return emitReal(arg, options, RealFormat::Float);
137137

138+
case 't':
139+
return emitTime(arg, options);
140+
138141
case 's':
139142
// Simplified handling for literals.
140143
if (auto *lit = arg.as_if<slang::ast::StringLiteral>()) {
@@ -207,6 +210,36 @@ struct FormatStringParser {
207210
return success();
208211
}
209212

213+
// Format an integer with the %t specifier according to IEEE 1800-2023
214+
// § 20.4.3 "$timeformat"
215+
LogicalResult emitTime(const slang::ast::Expression &arg,
216+
const FormatOptions &options) {
217+
218+
// Only handle `TimeType` values.
219+
auto value = context.convertRvalueExpression(
220+
arg, moore::TimeType::get(context.getContext()));
221+
if (!value)
222+
return failure();
223+
224+
mlir::IntegerAttr width = nullptr;
225+
if (options.width) {
226+
mlir::Type i32Ty =
227+
mlir::IntegerType::get(context.getContext(), /*width=*/32);
228+
width = mlir::IntegerAttr::get(i32Ty, options.width.value());
229+
}
230+
231+
// Delegate actual formatting to `moore.fmt.time`, annotate width if
232+
// provided
233+
if (width) {
234+
fragments.push_back(
235+
moore::FormatTimeOp::create(builder, loc, value, width));
236+
} else {
237+
fragments.push_back(moore::FormatTimeOp::create(builder, loc, value));
238+
}
239+
240+
return success();
241+
}
242+
210243
/// Emit an expression argument with the appropriate default formatting.
211244
LogicalResult emitDefault(const slang::ast::Expression &expr) {
212245
FormatOptions options;

test/Conversion/ImportVerilog/basic.sv

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3174,3 +3174,25 @@ function automatic int unsigned returnParameterArrayElement (int idx);
31743174
return ParameterArray[idx];
31753175
endfunction
31763176

3177+
// CHECK-LABEL: func.func private @TimeFormat(
3178+
function void TimeFormat();
3179+
// CHECK: [[TIME:%.+]] = moore.constant_time 100000000 fs
3180+
localparam time TestTime = 100ns;
3181+
// CHECK-NEXT: [[FMT:%.+]] = moore.fmt.time [[TIME]]
3182+
// CHECK-NEXT: [[LINEBREAK:%.+]] = moore.fmt.literal "\0A"
3183+
// CHECK-NEXT: [[CONCAT:%.+]] = moore.fmt.concat ([[FMT]], [[LINEBREAK]])
3184+
// CHECK-NEXT: moore.builtin.display [[CONCAT]]
3185+
$display("%t", TestTime);
3186+
// CHECK: [[SIMTIME:%.+]] = moore.builtin.time
3187+
// CHECK-NEXT: [[FMT2:%.+]] = moore.fmt.time [[SIMTIME]]
3188+
// CHECK-NEXT: [[LINEBREAK2:%.+]] = moore.fmt.literal "\0A"
3189+
// CHECK-NEXT: [[CONCAT2:%.+]] = moore.fmt.concat ([[FMT2]], [[LINEBREAK2]])
3190+
// CHECK-NEXT: moore.builtin.display [[CONCAT2]]
3191+
$display("%t", $time());
3192+
// CHECK: [[SIMTIME3:%.+]] = moore.builtin.time
3193+
// CHECK-NEXT: [[FMT3:%.+]] = moore.fmt.time [[SIMTIME3]], width 4
3194+
// CHECK-NEXT: [[LINEBREAK3:%.+]] = moore.fmt.literal "\0A"
3195+
// CHECK-NEXT: [[CONCAT3:%.+]] = moore.fmt.concat ([[FMT3]], [[LINEBREAK3]])
3196+
// CHECK-NEXT: moore.builtin.display [[CONCAT3]]
3197+
$display("%4t", $time());
3198+
endfunction

0 commit comments

Comments
 (0)