nickg / nvc

VHDL compiler and simulator
https://www.nickg.me.uk/nvc/
GNU General Public License v3.0
636 stars 80 forks source link

Verilog scheduling semantics for blocking assignment #1044

Open avelure opened 3 weeks ago

avelure commented 3 weeks ago

We are trying to simulate some simple clockgating blocks that are in verilog. The rest of the design is vhdl. These seem trivial to convert to VHDL, but the clockgating introduces deltacycle delays on the gated clock. The design has some signals passing from the non-gated to the gated domain clock, which would skip a cycle when we converted the module to vhdl. At least with modelsim the CLK and GCLK would allign on the same deltacycle.

module clk_gate_p (
    CLK,
    E,
    GCLK,
    SE
);
input CLK;
input E;
input SE;
output GCLK;

wire CLK;
wire E;
wire SE;
wire GCLK;
reg en1;

always @ (CLK or E or SE) begin
  if (CLK == 0)
    en1 = SE | E;
end

assign GCLK = CLK & en1;

endmodule
module clk_gate_n (
    CLK,
    E,
    GCLK,
    SE
);
input CLK;
input E;
input SE;
output GCLK;

wire CLK;
wire E;
wire SE;
wire GCLK;
reg en1;

always @ (CLK or E or SE) begin
  if (CLK == 1)
    en1 = SE | E;
end

assign GCLK = CLK | !en1;

endmodule
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity test is end entity;
architecture arch of test is
  component clk_gate_p is
  port (
    CLK : in std_logic;
    E : in std_logic;
    GCLK : in std_logic;
    SE : in std_logic);
  end component;
  component clk_gate_n is
  port (
    CLK : in std_logic;
    E : in std_logic;
    GCLK : in std_logic;
    SE : in std_logic);
  end component;
  signal CLK, SE, E, GCLK_p, GCLK_n : std_logic;
begin
  u0 : clk_gate_p
  port map (
    CLK => clk,
    E => e,
    GCLK => gclk_p,
    SE => se);
  u1 : clk_gate_n
  port map (
    CLK => clk,
    E => e,
    GCLK => gclk_n,
    SE => se);

  se <= '0';

  process is
  begin
      CLK <= '0';
      E <= '0';
      wait for 1 ps;
      CLK <= '1';
      wait for 1 ps;
      assert gclk_p = '0' report "p 1" severity failure;
    assert gclk_n = '1' report "n 1" severity failure;
      CLK <= '0';
      wait for 1 ps;
    assert gclk_p = '0' report "p 2" severity failure;
      assert gclk_n = '1' report "n 2" severity failure;
      E <= '1';
    wait for 1 ps;
    assert gclk_p = '0' report "p 3" severity failure;
      assert gclk_n = '1' report "n 3" severity failure;
      CLK <= '1';
      wait for 1 ps;
    assert gclk_p = '1' report "p 4" severity failure;
      assert gclk_n = '1' report "n 4" severity failure;
      CLK <= '0';
      wait for 1 ps;
    assert gclk_p = '0' report "p 5" severity failure;
      assert gclk_n = '0' report "n 5" severity failure;
    wait;
  end process;
end architecture;
$ nvc -a clk_gate_n.v
$ nvc -a clk_gate_p.v
$ nvc --std=08 -a test.vhd
$ nvc -e test -r
** Fatal: 0ms+0: missing body for NVC.VERILOG."or"(25NVC.VERILOG.T_LOGIC_ARRAY25NVC.VERILOG.T_LOGIC_ARRAY)25NVC.VERILOG.T_LOGIC_ARRAY

commenting out the clk_gate_n instanciation gives

** Fatal: 0ms+1: missing body for NVC.VERILOG."="(25NVC.VERILOG.T_LOGIC_ARRAY25NVC.VERILOG.T_LOGIC_ARRAY)19NVC.VERILOG.T_LOGIC
nickg commented 2 weeks ago

The test case passes now but I don't think it'll help you much as there is currently the a delta cycle delay before the update to the gated clock, as if you'd written it in VHDL. The problem is NVC doesn't currently implement Verilog's scheduling model where a blocking assignment should cause sensitive processes to be scheduled immediately in the current scheduling region.