6 Commits

7 changed files with 225 additions and 64 deletions
Split View
  1. +2
    -3
      README.md
  2. +13
    -11
      alu/alu.vhd
  3. +13
    -8
      alu/alu_t.sv
  4. +11
    -7
      counter/counter_t.sv
  5. +160
    -26
      vai_reg/properties.sv
  6. +22
    -0
      vai_reg/trace.gtkw
  7. +4
    -9
      vai_reg/vai_reg.vhd

+ 2
- 3
README.md View File

@ -1,16 +1,15 @@
The original repository is located on my own git-server at [https://git.goodcleanfun.de/tmeissner/formal_verification](https://git.goodcleanfun.de/tmeissner/formal_verification)
The original repository is located on my own git-server at [https://git.goodcleanfun.de/tmeissner/formal_hw_verification](https://git.goodcleanfun.de/tmeissner/formal_hw_verification)
It is mirrored to github with every push, so both should be in sync.
# formal_verification
Tests and examples of using formal verification to check correctness of digital hardware designs. All tests are done with SymbiYosys, a front-end for formal verification flows based on [Yosys](https://github.com/YosysHQ). Some examples use the VHDL/SystemVerilog parser plugin by Verific which isn't free SW and not included in the free Yosys version. See on the [Symbiotic EDA website](https://www.symbioticeda.com) for more information.
Tests and examples of using formal verification to check correctness of digital hardware designs. All tests are done with SymbiYosys, a front-end for formal verification flows based on [Yosys](https://github.com/YosysHQ). Some examples use the VHDL/SystemVerilog frontend plugin by Verific which isn't free SW and not included in the free Yosys version. See on the [Symbiotic EDA website](https://www.symbioticeda.com) for more information.
### alu
A simple ALU design in VHDL, together with a formal testbench written in SystemVerilog. The testbench contains various simple SVA properties used by assert & cover directives which are proved with the SymbiYosys tool.
### counter
A simple counter design in VHDL, together with a formal testbench written in SystemVerilog. The testbench contains various simple SVA properties used by assert & cover directives which are proved with the SymbiYosys tool.


+ 13
- 11
alu/alu.vhd View File

@ -24,10 +24,12 @@ end entity alu;
architecture rtl of alu is
constant c_add : std_logic_vector(1 downto 0) := "00";
constant c_sub : std_logic_vector(1 downto 0) := "01";
constant c_and : std_logic_vector(1 downto 0) := "10";
constant c_or : std_logic_vector(1 downto 0) := "11";
subtype t_opc is std_logic_vector(Opc_i'length-1 downto 0);
constant c_add : t_opc := "00";
constant c_sub : t_opc := "01";
constant c_and : t_opc := "10";
constant c_or : t_opc := "11";
begin
@ -40,14 +42,14 @@ begin
elsif (rising_edge(Clk_i)) then
case Opc_i is
when c_add => (OverFlow_o, Dout_o) <=
std_logic_vector(resize(unsigned(DinA_i), Dout_o'length+1) +
resize(unsigned(DinB_i), Dout_o'length+1));
std_logic_vector(resize(unsigned(DinA_i), Dout_o'length+1) +
resize(unsigned(DinB_i), Dout_o'length+1));
when c_sub => (OverFlow_o, Dout_o) <=
std_logic_vector(resize(unsigned(DinA_i), Dout_o'length+1) -
resize(unsigned(DinB_i), Dout_o'length+1));
when c_and => Dout_o <= DinA_i and DinB_i;
when c_or => Dout_o <= DinA_i or DinB_i;
when others => null;
std_logic_vector(resize(unsigned(DinA_i), Dout_o'length+1) -
resize(unsigned(DinB_i), Dout_o'length+1));
when c_and => Dout_o <= DinA_i and DinB_i;
when c_or => Dout_o <= DinA_i or DinB_i;
when others => null;
end case;
end if;
end process;


+ 13
- 8
alu/alu_t.sv View File

@ -41,24 +41,29 @@ module alu_t (
always @(posedge Clk_i)
init_state = 0;
default clocking
@(posedge Clk_i);
endclocking
default disable iff (!Reset_n_i);
bit unsigned [`WIDTH:0] dina, dinb;
assign dina = DinA_i;
assign dinb = DinB_i;
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Opc_i == `OPC_ADD |=> Dout_o == ($past(DinA_i) + $past(DinB_i)));
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Opc_i == `OPC_ADD && (dina + dinb) > 2**`WIDTH-1 |=> OverFlow_o);
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Opc_i == `OPC_SUB |=> Dout_o == ($past(DinA_i) - $past(DinB_i)));
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Opc_i == `OPC_SUB && (dina - dinb) > 2**`WIDTH-1 |=> OverFlow_o);
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Opc_i == `OPC_AND |=> Dout_o == ($past(DinA_i) & $past(DinB_i)));
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Opc_i == `OPC_OR |=> Dout_o == ($past(DinA_i) | $past(DinB_i)));
assert property (Opc_i == `OPC_ADD |=> Dout_o == ($past(DinA_i) + $past(DinB_i)));
assert property (Opc_i == `OPC_ADD && (dina + dinb) > 2**`WIDTH-1 |=> OverFlow_o);
assert property (Opc_i == `OPC_SUB |=> Dout_o == ($past(DinA_i) - $past(DinB_i)));
assert property (Opc_i == `OPC_SUB && (dina - dinb) > 2**`WIDTH-1 |=> OverFlow_o);
assert property (Opc_i == `OPC_AND |=> Dout_o == ($past(DinA_i) & $past(DinB_i)));
assert property (Opc_i == `OPC_OR |=> Dout_o == ($past(DinA_i) | $past(DinB_i)));
property cover_opc (opc);
@(posedge Clk_i)
disable iff (!Reset_n_i) Opc_i == opc;
Opc_i == opc;
endproperty
cover property (cover_opc(`OPC_ADD));


+ 11
- 7
counter/counter_t.sv View File

@ -38,19 +38,23 @@ module counter_t (
// Use global clock to constrain the DUT clock
always @($global_clock) begin
assume(Clk_i != $past(Clk_i));
assume (Clk_i != $past(Clk_i));
end
default clocking
@(posedge Clk_i);
endclocking
default disable iff (!Reset_n_i);
// Immediate assertions
always @(*)
if (!Reset_n_i) assert (Data_o == `INITVAL);
// Fails with unbounded prove using SMTBMC, maybe
// the assumptions/assertions have to be more strict.
// With abc pdr this can be successfully proved.
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Data_o < `ENDVAL |=> Data_o == $past(Data_o) + 1);
assert property (@(posedge Clk_i) disable iff (!Reset_n_i) Data_o == `ENDVAL |=> $stable(Data_o));
assert property (@(posedge Clk_i) Data_o >= `INITVAL && Data_o <= `ENDVAL);
// Concurrent assertions
assert property (Data_o < `ENDVAL |=> Data_o == $past(Data_o) + 1);
assert property (Data_o == `ENDVAL |=> $stable(Data_o));
assert property (Data_o >= `INITVAL && Data_o <= `ENDVAL);
endmodule


+ 160
- 26
vai_reg/properties.sv View File

@ -13,7 +13,10 @@ module properties (
input DoutAccept_i,
// Internals
input [2:0] s_fsm_state,
input [7:0] s_header
input [7:0] s_header,
input [7:0] s_data,
input s_error,
input [7:0] s_register [0:7]
);
@ -33,13 +36,22 @@ module properties (
always @(posedge Clk_i)
init_state = 0;
// Default clocking & reset
default clocking
@(posedge Clk_i);
endclocking
default disable iff (!Reset_n_i);
// Constraints
assume property (
DinValid_i && !DinAccept_o |=>
$stable(DinValid_i)
);
assume property (
DinValid_i && !DinAccept_o |=>
$stable(Din_i)
@ -58,31 +70,31 @@ module properties (
// Asserts
assert property (
fsm_state_valid_a : assert property (
s_fsm_state >= 0 && s_fsm_state <= 6
);
assert property (
valid_when_start_a : assert property (
DoutStart_o |->
DoutValid_o
);
assert property (
start_off_when_acked_a : assert property (
DoutStart_o && DoutAccept_i |=>
!DoutStart_o
);
assert property (
valid_when_stop_a : assert property (
DoutStop_o |->
DoutValid_o
);
assert property (
stop_off_when_acked_a : assert property (
DoutStop_o && DoutAccept_i |=>
!DoutStop_o
);
assert property (
store_header_a : assert property (
s_fsm_state == 1 && DinValid_i && DinStart_i && DinAccept_o |=>
s_header == $past(Din_i)
);
@ -90,80 +102,202 @@ module properties (
// State changes
assert property (disable iff (!Reset_n_i)
// IDLE -> GET_HEADER
fsm_idle_to_get_header_a : assert property (
s_fsm_state == 0 |=> s_fsm_state == 1
);
assert property (disable iff (!Reset_n_i)
// GET_HEADER -> GET_DATA
fsm_get_header_to_get_data_a : assert property (
s_fsm_state == 1 && DinValid_i && DinStart_i && DinStop_i && Din_i[3:0] == `READ |=>
s_fsm_state == 2
);
assert property (disable iff (!Reset_n_i)
// GET_HEADER -> SET_DATA
fsm_get_header_to_set_data_a : assert property (
s_fsm_state == 1 && DinValid_i && DinStart_i && !DinStop_i && Din_i[3:0] == `WRITE |=>
s_fsm_state == 3
);
assert property (disable iff (!Reset_n_i)
// GET_DATA -> SEND_HEADER
fsm_get_data_to_send_header_a : assert property (
s_fsm_state == 2 |=> s_fsm_state == 4
);
assert property (disable iff (!Reset_n_i)
// SET_DATA -> IDLE
fsm_set_data_to_idle_a : assert property (
s_fsm_state == 3 && DinValid_i && !DinStop_i |=> s_fsm_state == 0
);
// SET_DATA -> SEND_HEADER
fsm_set_data_to_send_header_a : assert property (
s_fsm_state == 3 && DinValid_i && DinStop_i |=> s_fsm_state == 4
);
// SEND_HEADER -> SEND_DATA
fsm_send_header_to_send_data_a : assert property (
s_fsm_state == 4 && DoutValid_o && DoutAccept_i && s_header[3:0] == `READ |=> s_fsm_state == 5
);
assert property (disable iff (!Reset_n_i)
s_fsm_state == 4 && DoutValid_o && DoutAccept_i && s_header[3:0] != `READ |=> s_fsm_state == 6
// SEND_HEADER -> SEND_FOOTER
fsm_send_header_to_send_footer_a : assert property (
s_fsm_state == 4 && DoutValid_o && DoutAccept_i && s_header[3:0] == `WRITE |=> s_fsm_state == 6
);
// SEND_DATA -> SEND_FOOTER
fsm_send_data_to_send_footer_a : assert property (
s_fsm_state == 5 && DoutValid_o && DoutAccept_i |=> s_fsm_state == 6
);
assert property (disable iff (!Reset_n_i)
// SEND_FOOTER -> IDLE
fsm_send_footer_to_idle_a : assert property (
s_fsm_state == 6 && DoutValid_o && DoutAccept_i |=> s_fsm_state == 0
);
// Protocol checks
assert property (
header_in_valid_range_a : assert property (
s_fsm_state > 1 |->
s_header[3:0] inside {`READ, `WRITE}
);
assert property (
header_stable_a : assert property (
s_fsm_state > 1 |=>
$stable(s_header)
);
header_dout_valid_a : assert property (
DoutStart_o && DoutValid_o |->
Dout_o[3:0] == s_header[3:0]
Dout_o == s_header
);
error_flag_initial_false_a : assert property (
s_fsm_state inside {1, 2, 3} |->
!s_error
);
error_flag_set_invalid_addr_a : assert property (
s_fsm_state >= 4 |->
s_error == !(s_header[7:4] <= 7)
);
assert property (
footer_dout_valid_a :assert property (
DoutStop_o && DoutValid_o |->
Dout_o == s_error
);
doutvalid_stable_until_acked_a : assert property (
DoutValid_o && !DoutAccept_i |=>
$stable(DoutValid_o)
);
dout_stable_until_acked_a : assert property (
DoutValid_o && !DoutAccept_i |=>
$stable(Dout_o)
);
assert property (
doutstart_stable_until_acked_a : assert property (
DoutValid_o && !DoutAccept_i |=>
$stable(DoutStart_o)
);
assert property (
doutstop_stable_until_acked_a : assert property (
DoutValid_o && !DoutAccept_i |=>
$stable(DoutStop_o)
);
assert property (
DoutValid_o |-> !(DoutStart_o && DoutStop_o)
onehot_doutstart_doutstop_a : assert property (
DoutValid_o |-> $onehot0({DoutStart_o, DoutStop_o})
);
assert property (
doutstart_in_valid_fsm_state_a : assert property (
DoutStart_o |-> s_fsm_state == 4
);
assert property (
doutstop_in_valid_fsm_state_a : assert property (
DoutStop_o |-> s_fsm_state == 6
);
assert property (
doutvalid_in_valid_fsm_states_a : assert property (
DoutValid_o |-> s_fsm_state >= 4 && s_fsm_state <= 6
);
// Write ack frame
write_frame_a : assert property (
DoutValid_o && DoutStart_o && DoutAccept_i && Dout_o[3:0] == `WRITE |=>
!DoutValid_o ##1
DoutValid_o && DoutStop_o
);
// Read ack frame
read_frame_a : assert property (
DoutValid_o && DoutStart_o && DoutAccept_i && Dout_o[3:0] == `READ |=>
!DoutValid_o ##1
DoutValid_o && !DoutStart_o && !DoutStop_o && !DoutAccept_i [*] ##1
DoutValid_o && !DoutStart_o && !DoutStop_o && DoutAccept_i ##1
!DoutValid_o ##1
DoutValid_o && DoutStop_o
);
// Register read in GET_DATA if valid adress was given
get_data_valid_a : assert property (
s_fsm_state > 2 && s_header[7:4] <= 7 && s_header[3:0] == `READ |->
s_data == s_register[s_header[7:4]]
);
// Error flag set & no register read in GET_DATA if invalid adress was given
get_data_invalid_a : assert property (
s_fsm_state > 2 && s_header[7:4] > 7 && s_header[3:0] == `READ |=>
s_data == 0 && s_error
);
// register stable if read request
reg_stable_during_read_a : assert property (
s_fsm_state > 1 && s_header[3:0] == `READ |=>
$stable(s_register)
);
// Read ack data correct if address is valid
read_ack_data_valid_a : assert property (
DoutValid_o && DoutStart_o && DoutAccept_i && Dout_o[3:0] == `READ && s_header[7:4] <= 7 |=>
!DoutValid_o ##1
DoutValid_o && !DoutStart_o && !DoutStop_o && Dout_o == s_data
);
// Read ack data zero if address is invalid
read_ack_data_invalid_a : assert property (
DoutValid_o && DoutStart_o && DoutAccept_i && Dout_o[3:0] == `READ && s_header[7:4] > 7 |=>
!DoutValid_o ##1
DoutValid_o && !DoutStart_o && !DoutStop_o && Dout_o == 0
);
// Register write in SET_DATA if valid adress was given
set_data_valid_a : assert property (
s_fsm_state == 3 && DinValid_i && DinAccept_o && DinStop_i && s_header[7:4] <= 7 |=>
s_register[s_header[7:4]] == $past(Din_i)
);
// Error flag set & no register write in SET_DATA if invalid adress was given
set_data_invalid_a : assert property (
s_fsm_state == 3 && DinValid_i && DinAccept_o && DinStop_i && s_header[7:4] > 7 |=>
$stable(s_register) && s_error
);
// No register write in SET_DATA if stop don't active
set_data_discard_a : assert property (
s_fsm_state == 3 && DinValid_i && DinAccept_o && !DinStop_i |=>
$stable(s_register)
);
fsm_read_req_when_get_data_a : assert property (
s_fsm_state == 2 |-> s_header[3:0] == `READ
);
fsm_write_req_when_set_data_a : assert property (
s_fsm_state == 3 |-> s_header[3:0] == `WRITE
);
endmodule


+ 22
- 0
vai_reg/trace.gtkw View File

@ -0,0 +1,22 @@
smt_step
-Global
vai_reg.Reset_n_i
vai_reg.properties.Clk_i
-VAI req
vai_reg.properties.DinStart_i
vai_reg.properties.DinStop_i
vai_reg.properties.Din_i[7:0]
vai_reg.properties.DinValid_i
vai_reg.properties.DinAccept_o
-VAI ack
vai_reg.properties.DoutStart_o
vai_reg.properties.DoutStop_o
vai_reg.properties.Dout_o[7:0]
vai_reg.properties.DoutValid_o
vai_reg.properties.DoutAccept_i
-Internal
vai_reg.properties.s_fsm_state[2:0]
vai_reg.properties.s_header[7:0]
vai_reg.properties.s_data[7:0]
vai_reg.properties.s_register[63:0]
vai_reg.properties.s_error

+ 4
- 9
vai_reg/vai_reg.vhd View File

@ -50,8 +50,7 @@ architecture rtl of vai_reg is
begin
s_dout_accepted <= true when DoutValid_o = '1' and DoutAccept_i = '1' else
false;
s_dout_accepted <= (DoutValid_o and DoutAccept_i) = '1';
process (Reset_n_i, Clk_i) is
@ -79,18 +78,19 @@ begin
s_header <= (others => '0');
s_data <= (others => '0');
s_error <= false;
DinAccept_o <= '1';
s_fsm_state <= GET_HEADER;
when GET_HEADER =>
DinAccept_o <= '1';
if (DinValid_i = '1' and DinStart_i = '1') then
DinAccept_o <= '0';
s_header <= Din_i;
if (Din_i(3 downto 0) = C_READ and DinStop_i = '1') then
DinAccept_o <= '0';
s_fsm_state <= GET_DATA;
elsif (Din_i(3 downto 0) = C_WRITE and DinStop_i = '0') then
s_fsm_state <= SET_DATA;
else
DinAccept_o <= '0';
s_fsm_state <= IDLE;
end if;
end if;
@ -105,16 +105,11 @@ begin
s_fsm_state <= SEND_HEADER;
when SET_DATA =>
DinAccept_o <= '1';
if (DinValid_i = '1') then
DinAccept_o <= '0';
if (DinStop_i = '1') then
if (unsigned(a_addr) <= 7) then
-- Following line results in a Segmentation Fault
s_register(to_integer(unsigned(a_addr))) <= Din_i;
-- Following line results in following error:
-- ERROR: Unsupported cell type $dlatchsr for cell $verific$wide_dlatchrs_8.$verific$i1$172.
s_register(0) <= Din_i;
else
s_error <= true;
end if;


Loading…
Cancel
Save