diff --git a/include/verilated_std.sv b/include/verilated_std.sv index 13c3f6ad6..b892f728e 100644 --- a/include/verilated_std.sv +++ b/include/verilated_std.sv @@ -102,6 +102,9 @@ package std; // IEEE 1800-specified standard "semaphore" class semaphore; protected int m_keyCount; + protected int m_nextKeyCount = '1; + protected longint unsigned m_ticket = 0; + protected longint unsigned m_nextTicket = 0; function new(int keyCount = 0); m_keyCount = keyCount; @@ -113,19 +116,26 @@ package std; task get(int keyCount = 1); `ifdef VERILATOR_TIMING - while (m_keyCount < keyCount) begin - wait (m_keyCount >= keyCount); + longint unsigned ticket; + // Fast path: take if keys fit AND either no one is queued, or + // the head still doesn't fit (so we're not stealing its keys). + if (m_keyCount >= keyCount && m_nextKeyCount > m_keyCount) begin + m_keyCount -= keyCount; + return; end + ticket = m_nextTicket++; + wait (m_ticket == ticket); + m_nextKeyCount = keyCount; + wait (m_keyCount >= keyCount); m_keyCount -= keyCount; + m_ticket++; `endif endtask function int try_get(int keyCount = 1); - if (m_keyCount >= keyCount) begin - m_keyCount -= keyCount; - return 1; - end - return 0; + if (m_keyCount < keyCount) return 0; + m_keyCount -= keyCount; + return 1; endfunction endclass diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index c8d729ba9..d06a59bbc 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -1162,6 +1162,17 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst { } iterateAndNextConstNull(nodep->stmtsp()); } + void visit(AstWait* nodep) override { + puts("wait("); + iterateConst(nodep->condp()); + puts(")"); + if (nodep->stmtsp()) { + puts(" "); + iterateAndNextConstNull(nodep->stmtsp()); + } else { + puts(";\n"); + } + } void visit(AstCAwait* nodep) override { AstCMethodHard* methodp = VN_CAST(nodep->exprp(), CMethodHard); UASSERT_OBJ(methodp, nodep, "AstCAwait expression must be an AstCMethodHard"); diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index a6c063982..d551f8faa 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -77,6 +77,7 @@ module Vt_debug_emitv_t; end #1; $write("After #1 delay"); + wait((clk == 'sh1)) $write("After wait(clk == 1)"); end end bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; @@ -757,6 +758,12 @@ endmodule package Vt_debug_emitv_std; class Vt_debug_emitv_semaphore; int signed m_keyCount; + int signed m_nextKeyCount; + m_nextKeyCount = 32'hffffffff; + longint m_ticket; + m_ticket = 64'h0; + longint m_nextTicket; + m_nextTicket = 64'h0; function new; input int signed keyCount; m_keyCount = keyCount; @@ -766,29 +773,41 @@ package Vt_debug_emitv_std; m_keyCount = (m_keyCount + keyCount); endtask task get; + longint __Vincrement1; input int signed keyCount; - while ((m_keyCount < keyCount)) begin - begin - - ???? // WAIT - (m_keyCount >= keyCount)end - end - m_keyCount = (m_keyCount - keyCount); - endtask - function try_get; - input int signed keyCount; + longint ticket; begin : label3 - try_get = /*CRESET*/; - if ((m_keyCount >= keyCount)) begin + ticket = /*CRESET*/; + if (((m_keyCount >= keyCount) && (m_nextKeyCount + > + m_keyCount))) begin begin m_keyCount = (m_keyCount - keyCount); - try_get = 'sh1; disable label3; end end - try_get = 'sh0; - disable label3; + __Vincrement1 = m_nextTicket; + m_nextTicket = (m_nextTicket + 64'h1); + ticket = __Vincrement1; + wait((m_ticket == ticket)); + m_nextKeyCount = keyCount; + wait((m_keyCount >= keyCount)); + m_keyCount = (m_keyCount - keyCount); + m_ticket = (m_ticket + 64'h1); + end + endtask + function try_get; + input int signed keyCount; + begin : label4 + try_get = /*CRESET*/; + if ((m_keyCount < keyCount)) begin + try_get = 'sh0; + disable label4; + end + m_keyCount = (m_keyCount - keyCount); + try_get = 'sh1; + disable label4; end endfunction endclass @@ -807,12 +826,12 @@ package Vt_debug_emitv_std; p ???? // CLASSREFDTYPE 'process' ; - begin : label4 + begin : label5 self = /*CRESET*/; p = new(); $c(p.m_process = vlProcess;); self = p; - disable label4; + disable label5; end endfunction task set_status; @@ -820,10 +839,10 @@ package Vt_debug_emitv_std; $c(m_process->state(s);); endtask function status; - begin : label5 + begin : label6 status = /*CRESET*/; status = ($c(m_process->state())); - disable label5; + disable label6; end endfunction task kill; @@ -837,12 +856,9 @@ package Vt_debug_emitv_std; set_status(process::RUNNING); endtask task await; - - ???? // WAIT - ((status() == process::FINISHED) || (status() - == - process:: - KILLED))endtask + wait(((status() == process::FINISHED) || + (status() == process::KILLED))); + endtask task killQueue; ref ???? // CLASSREFDTYPE 'process' @@ -898,12 +914,12 @@ package Vt_debug_emitv_std; ???? // SYSTEMCSECTION function get_randstate; string s; - begin : label6 + begin : label7 get_randstate = /*CRESET*/; s = string'($c(0)); $c(s = m_process->randstate();); get_randstate = s; - disable label6; + disable label7; end endfunction task set_randstate; @@ -989,11 +1005,11 @@ package Vt_debug_emitv___024unit; ???? // CLASSREFDTYPE 'Cls' ; input int signed member; - begin : label7 + begin : label8 rand_restricted = randomize() with ( ???? // CONSTRAINTEXPR (item.rmember1 < member)) ; - disable label7; + disable label8; end endfunction endpackage @@ -1014,13 +1030,13 @@ module Vt_debug_emitv_sub; endtask function f; input int signed v; - begin : label8 + begin : label9 if ((v == 'sh0)) begin f = 'sh21; - disable label8; + disable label9; end f = ({32'h1{{31'h0, v[2]}}} + 32'h1); - disable label8; + disable label9; end endfunction real r; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index adebc64c6..a98cf4929 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -93,6 +93,7 @@ module t (/*AUTOARG*/ if (|downto_32[60-:7]) $write(""); if (the_ifaces[2].ifsig) $write(""); #1 $write("After #1 delay"); + wait(clk == 1) $write("After wait(clk == 1)"); end bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; diff --git a/test_regress/t/t_semaphore_fair.out b/test_regress/t/t_semaphore_fair.out new file mode 100644 index 000000000..ccb697bdf --- /dev/null +++ b/test_regress/t/t_semaphore_fair.out @@ -0,0 +1,13 @@ +[0] DRV locked +[5] DRV unlocked +[5] MON locked +[5] MON unlocked +[5] DRV locked +[10] DRV unlocked +[10] MON locked +[10] MON unlocked +[10] DRV locked +[15] DRV unlocked +[15] MON locked +[15] MON unlocked +*-* All Finished *-* diff --git a/test_regress/t/t_semaphore_fair.py b/test_regress/t/t_semaphore_fair.py new file mode 100755 index 000000000..9aad707de --- /dev/null +++ b/test_regress/t/t_semaphore_fair.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_semaphore_fair.v b/test_regress/t/t_semaphore_fair.v new file mode 100644 index 000000000..2f1eb0e2b --- /dev/null +++ b/test_regress/t/t_semaphore_fair.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns / 1ns +module t; + semaphore sem = new(1); + integer i; + + task driver; + repeat (3) begin + sem.get(1); + $display("[%0t] DRV locked", $time); + #5 sem.put(1); + $display("[%0t] DRV unlocked", $time); + end + endtask + + task monitor; + for (i = 0; i < 3; i = i + 1) begin + #1 sem.get(1); + $display("[%0t] MON locked", $time); + sem.put(1); + $display("[%0t] MON unlocked", $time); + end + endtask + + initial begin + fork + driver(); + monitor(); + join + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_semaphore_fifo_block.out b/test_regress/t/t_semaphore_fifo_block.out new file mode 100644 index 000000000..80325e0ec --- /dev/null +++ b/test_regress/t/t_semaphore_fifo_block.out @@ -0,0 +1,7 @@ +[3] C got 1 +[3] C put 1 +[4] D put 2 +[4] A got 4 +[5] A put 3 +[5] B got 3 +*-* All Finished *-* diff --git a/test_regress/t/t_semaphore_fifo_block.py b/test_regress/t/t_semaphore_fifo_block.py new file mode 100755 index 000000000..9aad707de --- /dev/null +++ b/test_regress/t/t_semaphore_fifo_block.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_semaphore_fifo_block.v b/test_regress/t/t_semaphore_fifo_block.v new file mode 100644 index 000000000..44d163caf --- /dev/null +++ b/test_regress/t/t_semaphore_fifo_block.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns / 1ns +module t; + semaphore sem = new(2); + + initial begin + fork + begin + #1 sem.get(4); + $write("[%0t] A got 4\n", $time); + #1 + sem.put(3); + $write("[%0t] A put 3\n", $time); + end + begin + #2 sem.get(3); + $write("[%0t] B got 3\n", $time); + end + begin + #3 sem.get(1); + $write("[%0t] C got 1\n", $time); + sem.put(1); + $write("[%0t] C put 1\n", $time); + end + begin + #4 sem.put(2); + $write("[%0t] D put 2\n", $time); + end + join + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule