IEEE-compliant, fair `std::semaphore` (#7435) (#7605)

Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
Krzysztof Bieganski 2026-05-18 11:11:42 +02:00 committed by GitHub
parent 00c9e58006
commit eb258d7df7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 210 additions and 39 deletions

View File

@ -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

View File

@ -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");

View File

@ -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;

View File

@ -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];

View File

@ -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 *-*

View File

@ -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()

View File

@ -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

View File

@ -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 *-*

View File

@ -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()

View File

@ -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