Illegal output port connection

I am doing a timer ip project.

// timer.v
// Code your design here
// Code your design here
`include "register_control.sv"
`include "logic_control.sv"
`include "counter.sv"


module timer #(parameter ADDR_WIDTH = 8,
             parameter DATA_WIDTH = 8,
             parameter WAIT = 0
			 )
  (pclk, presetn, psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr, s_ovf, s_udf, clk_in1, clk_in2, clk_in3, clk_in4);
  
  input wire 					pclk;
  input wire 					presetn;
  input wire 					psel;
  input wire 					penable;
  input wire 					pwrite;
  input wire [ADDR_WIDTH-1:0] 	paddr;
  input wire [DATA_WIDTH-1:0]	pwdata;
  
  output reg [DATA_WIDTH-1:0]	prdata; 
  output reg					pready;
  output reg					pslverr;
  
  output reg 					s_ovf;
  output reg					s_udf;
  
  input wire 					clk_in1;
  input wire 					clk_in2;
  input wire 					clk_in3;
  input wire 					clk_in4;
  
  reg [DATA_WIDTH-1:0]			tdr_reg;
  reg [DATA_WIDTH-1:0]			tcr_reg;
  reg [DATA_WIDTH-1:0]			tsr_reg;
  
  wire 							clk_in;
  wire 							up_down;
  wire    						enable;
  wire							load_tdr;
  
  reg [DATA_WIDTH-1:0]			cnt;
  
  register_control #(.WAIT(WAIT)) dut1 (
    .pclk			(pclk),
    .presetn		(presetn),
    .psel			(psel),
    .penable		(penable),
    .pwrite			(pwrite),
    .paddr			(paddr),
    .pwdata			(pwdata),
    .prdata			(prdata),
    .pready			(pready),
    .pslverr		(pslverr),
    .s_ovf			(s_ovf),
    .s_udf			(s_udf),
    .tdr_reg		(tdr_reg),
    .tcr_reg		(tcr_reg),
    .tsr_reg		(tsr_reg)
  );
  
  logic_control dut2 (
    .clk_in1		(clk_in1),
    .clk_in2		(clk_in2),
    .clk_in3		(clk_in3),
    .clk_in4		(clk_in4),
    .tcr_reg		(tcr_reg),
    .clk_in			(clk_in),
    .up_down		(up_down),
    .enable			(enable),
    .load_tdr		(load_tdr)
  );
  
  counter dut3 (
    .pclk			(pclk),
    .presetn		(presetn),
    .clk_in			(clk_in), 
    .up_down		(up_down), 
    .tdr_reg		(tdr_reg), 
    .enable			(enable), 
    .load_tdr		(load_tdr), 
    .cnt			(cnt), 
    .s_ovf			(s_ovf), 
    .s_udf			(s_udf)
  );
endmodule
// Code your design here
module register_control #(parameter ADDR_WIDTH = 8,
                         parameter DATA_WIDTH = 8,
                         parameter WAIT = 0
                         )
  (pclk, presetn, psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr, tdr_reg, tcr_reg, tsr_reg, s_ovf, s_udf);
  input wire 					pclk;
  input wire 					presetn;
  input wire 					psel;
  input wire 					penable;
  input wire 					pwrite;
  input wire [ADDR_WIDTH-1:0] 	paddr;
  input wire [DATA_WIDTH-1:0]	pwdata;
  output reg [DATA_WIDTH-1:0]	prdata;
  output reg					pready;
  output reg					pslverr;
  
  input wire 					s_ovf;
  input wire					s_udf;
  
  output reg [DATA_WIDTH-1:0]	tdr_reg;
  output reg [DATA_WIDTH-1:0]	tcr_reg;
  output reg [DATA_WIDTH-1:0]	tsr_reg;

  localparam 	IDLE = 2'b00,
  				SETUP = 2'b01,
  				ACCESS = 2'b10;
  
  reg [WAIT-1:0] 				wait_signal;
  
  reg [1:0] current_state;
  reg [1:0] next_state;

  always @(*) begin
    case(current_state)
    	IDLE: begin
          wait_signal = WAIT + 1;
          pready = 1'b0;
          pslverr = 1'b0;
          
          if(psel & ~penable) begin
            next_state = SETUP;
          end else begin
            next_state = IDLE;
          end
        end
      	
      	SETUP: begin
          if(psel & penable) begin
            
            if(wait_signal == 0) begin
              pready = 1'b1;
              if(paddr > 8'h02) begin
                pslverr = 1'b1;
              end
              next_state = IDLE;
            end else begin
              next_state = ACCESS;
            end
          
          end else begin
            next_state = SETUP;
          end
        end
      
      	ACCESS: begin
          if(wait_signal != 0) begin
            next_state = ACCESS;
          end else begin
            pready = 1'b1;
            if(paddr > 8'h02) begin
              pslverr = 1'b1;
            end
            next_state = IDLE;
          end
        end
      	
      	default:
          next_state = IDLE;
    endcase
  end
  
  always @(posedge pclk or negedge presetn) begin
    if(~presetn) begin
      current_state <= IDLE;
    end else begin
      current_state <= next_state;
      wait_signal <= wait_signal - 1;
    end
  end

  always @(posedge pclk or negedge presetn) begin
    if(~presetn) begin
      tdr_reg <= 8'h00;
      tcr_reg <= 8'h00;
      tsr_reg <= 8'h00;
    end else begin
      // write transaction
      if(pready & psel & penable & pwrite) begin
        
        // configure tdr register
        tdr_reg <= (paddr == 8'h00) ? pwdata : tdr_reg;
        
        // configure tcr register
        if(paddr == 8'h01) begin
          tcr_reg[7] <= pwdata[7];
          tcr_reg[6] <= 1'b0;
          tcr_reg[5:4] <= pwdata[5:4];
          tcr_reg[3:2] <= 2'b00;
          tcr_reg[1:0] <= pwdata[1:0];
        end
        
        // configure tsr register
        if(paddr == 8'h02) begin
//           if(tsr_reg[1] && pwdata[1]) tsr_reg[1] <= 0;
//           else if (tsr_reg[0] && pwdata[0]) tsr_reg[0] <= 0;
//           else begin
//             tsr_reg[0] <= tsr_reg[0];
//             tsr_reg[1] <= tsr_reg[1];
//           end
//           tsr_reg[DATA_WIDTH-1:2] = pwdata[DATA_WIDTH-1:2];
          
//           if((tsr_reg[0] & tsr_reg[1]) & (~pwdata[0] & ~pwdata[1])) begin
//             tsr_reg[1:0] <= 2'b11;
//           end else begin
//             tsr_reg[1:0] <= 2'b00;
//           end
          // Nếu chỉ có 1 cặp tsr và pwdata == 1 thì sao? Cách bên dưới vẫn sai
          if((tsr_reg[0] & tsr_reg[1]) & (pwdata[0] & pwdata[1])) begin
            tsr_reg[1:0] <= 2'b00;
          end else begin
            tsr_reg[1:0] <= tsr_reg[1:0];
          end
          
          tsr_reg[DATA_WIDTH-1:2] <= 0;
        end
      // read transaction
      
      end else if(pready & psel & penable & ~pwrite) begin
        case(paddr)
          8'h00: prdata <= tdr_reg;
          8'h01: prdata <= tcr_reg;
          8'h02: prdata <= tsr_reg;
        endcase
      end else begin
      tdr_reg <= tdr_reg;
      tcr_reg <= tcr_reg;
      tsr_reg <= tsr_reg;
      end
    end
  end

  always @(*) begin
    tsr_reg[0] = s_ovf;
    tsr_reg[1] = s_udf;
  end
      
endmodule
// Code your design here
module logic_control #(parameter DATA_WIDTH = 8)(clk_in1, clk_in2, clk_in3, clk_in4, tcr_reg, clk_in, up_down, enable, load_tdr);
  
  input wire 					clk_in1;
  input wire 					clk_in2;
  input wire 					clk_in3;
  input wire 					clk_in4;
  input wire [DATA_WIDTH-1:0] 	tcr_reg;
  
  output reg 					clk_in;
  output reg 					up_down;
  output reg					enable;
  output reg					load_tdr;
  
  reg [1:0] sel;
  
  always @(*) begin
    sel = tcr_reg[1:0];
  
//     $display("sel is %0b", sel);
    
    case(sel) 
      2'b00: clk_in = clk_in1;
      2'b01: clk_in = clk_in2;
      2'b10: clk_in = clk_in3;
      2'b11: clk_in = clk_in4;
      default: $display("Invalid tcr_reg[1:0]!!!");
    endcase    
  	
//     $display("clk_in is %0d", clk_in);
    
    up_down = tcr_reg[5];
    enable = tcr_reg[4];
    load_tdr = tcr_reg[7];
    
//     $display("up_down is %0d", up_down);
//     $display("enable is %0d", enable);
//     $display("load_tdr is %0d", load_tdr);
    
  end
endmodule
module counter #(parameter DATA_WIDTH = 8) (pclk, presetn, clk_in, up_down, tdr_reg, enable, load_tdr, cnt, s_ovf, s_udf);
  
  input wire 					pclk;
  input wire					presetn;
  input wire 					clk_in;
  input wire 					up_down;
  input wire [DATA_WIDTH-1:0] 	tdr_reg;
  input wire					enable;
  input wire 					load_tdr;
  output reg [DATA_WIDTH-1:0]	cnt;
  output reg					s_ovf;
  output reg					s_udf;
  
  reg delay_clk_in;
  wire posedge_detector;
  
  integer count = 0;
  
  always @(*) begin
    $display("updown %0d", up_down);
    $display("posedge_detector %0d", posedge_detector);
	$display("clk_in %0d", clk_in);
    $display("combinational cnt: %0d at %0t", cnt, $time);
    
    if(load_tdr && count == 0) begin
      $display("combinational 1st cnt: %0d at %0t", cnt, $time);
      cnt = tdr_reg;
      count = count + 1;
      $display("combinational 2nd cnt: %0d at %0t", cnt, $time);
    end else begin
      cnt = cnt;
      $display("combinational 3rd cnt: %0d at %0t", cnt, $time);
    end
  end

  always @(posedge pclk or negedge presetn) begin
    if(~presetn) begin
    	cnt <= 8'h00;
    end else begin
      if(enable) begin
        if(up_down && posedge_detector) begin
          cnt <= cnt + 8'h01;
          $display("sequential cnt: %0d at %0t", cnt, $time);
        end else if(~up_down && posedge_detector) begin
          cnt <= cnt - 8'h01;
          $display("sequential cnt: %0d at %0t", cnt, $time);
        end
      end else begin
        cnt <= cnt;
      end
    end
  end
  
  always @(posedge pclk or negedge presetn) begin
    if(~presetn) begin
      s_udf <= 0;
      s_ovf <= 0;
    end else begin
      if(~up_down && (cnt == 8'hff)) begin
        s_udf <= 1;
      end else if(up_down && (cnt == 8'h00)) begin
        s_ovf <= 1;
      end else begin
        s_udf <= s_udf;
        s_ovf <= s_ovf;
      end
    end
  end
    
  always @(posedge pclk or negedge presetn) begin
	delay_clk_in <= clk_in;
  end
  
  assign posedge_detector = clk_in & ~delay_clk_in;  
endmodule

and this is the testbench file

// Code your testbench here
// or browse Examples

module tb_timer;
  
  parameter ADDR_WIDTH = 8;
  parameter DATA_WIDTH = 8;
  parameter WAIT = 3;
  
  reg 					pclk;
  reg 					presetn;
  reg 					psel;
  reg 					penable;
  reg 					pwrite;
  reg [ADDR_WIDTH-1:0]	paddr;
  reg [DATA_WIDTH-1:0]	pwdata;
  
  wire [DATA_WIDTH-1:0]	prdata;
  wire 					pready;
  wire 					pslverr;
  
  wire 					s_ovf;
  wire 					s_udf;
  
  reg 					clk_in1;
  reg 					clk_in2;
  reg 					clk_in3;
  reg 					clk_in4;
  
  integer count;
  integer i;

  timer #(.WAIT(WAIT)) dut (
    .pclk			(pclk),
    .presetn		(presetn),
    .psel			(psel),
    .penable		(penable),
    .pwrite			(pwrite),
    .paddr			(paddr),
    .pwdata			(pwdata),
    .prdata			(prdata),
    .pready			(pready),
    .pslverr		(pslverr),
    .s_ovf			(s_ovf),
    .s_udf			(s_udf),
    .clk_in1		(clk_in1),
    .clk_in2		(clk_in2),
    .clk_in3		(clk_in3),
    .clk_in4		(clk_in4)
  );
  
  task apb_write;
    input [ADDR_WIDTH-1:0] addr;
    input [DATA_WIDTH-1:0] data_in;
    
    begin
      @(posedge pclk);
      psel <= 1;
      paddr <= addr;
      pwdata <= data_in;
      pwrite <= 1;
      
      @(posedge pclk);
      penable <= 1;
      
      wait(pready);
      
      @(posedge pclk);
      psel <= 0;
      penable <= 0;
      pwrite <= 0;
    end
  endtask
  
  task apb_read;
    input [ADDR_WIDTH-1:0] addr;
    
    begin
      @(posedge pclk);
      psel <= 1;
      paddr <= addr;
      pwrite <= 0;
      
      @(posedge pclk);
      penable <= 1;
      
      wait(pready);
      
      @(posedge pclk);
      psel <= 0;
      penable <= 0;
    end
  endtask
  
  // clock generator
  initial begin
    pclk = 0;
    clk_in1 = 0;
    clk_in2 = 0;
    clk_in3 = 0;
    clk_in4 = 0;
    count = 0;
  end
  
  always #5 pclk = ~pclk;
  
  always @(posedge pclk) begin
    count <= count + 1;
    
    if (count % 1 == 0) clk_in1 <= ~clk_in1;
    if (count % 2 == 0) clk_in2 <= ~clk_in2;
    if (count % 4 == 0) clk_in3 <= ~clk_in3;
    if (count % 8 == 0) clk_in4 <= ~clk_in4;
  end
  /////////////////
  
  initial begin
    presetn = 0;
    
    psel = 0;
    penable = 0;
    pwrite = 0;
    paddr = 0;
    pwdata = 0;
    
    #20;
    presetn = 1;
    
    // write transaction
    apb_write(8'h00, 8'b11111000);
    
    apb_write(8'h01, 8'b11111100);
    
    apb_write(8'h01, 8'b11111101);
    
    apb_write(8'h01, 8'b11111110);

    apb_write(8'h01, 8'b11111111);
    
    apb_write(8'h01, 8'b11111100);
    
    apb_write(8'h01, 8'b11011100);
    
    apb_write(8'h01, 8'b11101111);
    
    apb_write(8'h02, 8'b11010000);
    
    apb_write(8'h02, 8'b11010011);
    
    // read transaction
    @(posedge pclk);
    apb_read(8'h00);
    
    apb_read(8'h02);
    
    apb_read(8'h01);
    
    apb_read(8'h07);

    #2000;
    $finish;
  end
  
  
  
  initial begin
    // Dump waves
    $dumpfile("dump.vcd");
    $dumpvars(1, tb_timer);
    $dumpvars(1, dut);
    $dumpvars(1, dut.dut1);
    $dumpvars(1, dut.dut2);
    $dumpvars(1, dut.dut3);
  end
endmodule

I test the code and it runs very well in edaplayground.
But when I test the project in questasim-64 10.2 I get errors. I do not know to fix it. This is the error.

** Error: (vopt-8884) timer_ip/timer.v(62): Illegal output port connection for "'prdata' (8th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(62): Illegal output port connection for "'pready' (9th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(62): Illegal output port connection for "'pslverr' (10th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(62): Illegal output port connection for "'tdr_reg' (13th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(62): Illegal output port connection for "'tcr_reg' (14th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(62): Illegal output port connection for "'tsr_reg' (15th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(87): Illegal output port connection for "'cnt' (8th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(87): Illegal output port connection for "'s_ovf' (9th connection)" to reg type. 
# 
# ** Error: (vopt-8884) timer_ip/timer.v(87): Illegal output port connection for "'s_udf' (10th connection)" to reg type. 
# 
# Optimization failed
# Error loading design

Your issue is likely due to the filename timer.v having a *.v suffix, which indicates that it is interpreted as Verilog rather than SystemVerilog. Your code involves instantiating modules and connecting their output ports to variables, which is permitted in SystemVerilog but not in Verilog.

Hi @dave_59,

//`include "register_control.sv"
//`include "logic_control.sv"
//`include "counter.sv"


module timer #(parameter ADDR_WIDTH = 8,
             parameter DATA_WIDTH = 8,
             parameter WAIT = 0
			 )
  (pclk, presetn, psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr, s_ovf, s_udf, clk_in1, clk_in2, clk_in3, clk_in4);
  
  input wire 					pclk;
  input wire 					presetn;
  input wire 					psel;
  input wire 					penable;
  input wire 					pwrite;
  input wire [ADDR_WIDTH-1:0] 	paddr;
  input wire [DATA_WIDTH-1:0]	pwdata;
  
  output reg [DATA_WIDTH-1:0]	prdata; 
  output reg					pready;
  output reg					pslverr;
  
  output reg 					s_ovf;
  output reg					s_udf;
  
  input wire 					clk_in1;
  input wire 					clk_in2;
  input wire 					clk_in3;
  input wire 					clk_in4;
  
  reg [DATA_WIDTH-1:0]			tdr_reg;
  reg [DATA_WIDTH-1:0]			tcr_reg;
  reg [DATA_WIDTH-1:0]			tsr_reg;
  
  wire 							clk_in;
  wire 							up_down;
  wire    						enable;
  wire							load_tdr;
  
  reg [DATA_WIDTH-1:0]			cnt;
  
  register_control #(.WAIT(WAIT)) dut1 (
    .pclk			(pclk),
    .presetn		(presetn),
    .psel			(psel),
    .penable		(penable),
    .pwrite			(pwrite),
    .paddr			(paddr),
    .pwdata			(pwdata),
    .prdata			(prdata),
    .pready			(pready),
    .pslverr		(pslverr),
    .s_ovf			(s_ovf),
    .s_udf			(s_udf),
    .tdr_reg		(tdr_reg),
    .tcr_reg		(tcr_reg),
    .tsr_reg		(tsr_reg)
  );
  
  logic_control dut2 (
    .clk_in1		(clk_in1),
    .clk_in2		(clk_in2),
    .clk_in3		(clk_in3),
    .clk_in4		(clk_in4),
    .tcr_reg		(tcr_reg),
    .clk_in			(clk_in),
    .up_down		(up_down),
    .enable			(enable),
    .load_tdr		(load_tdr)
  );
  
  counter dut3 (
    .pclk			(pclk),
    .presetn		(presetn),
    .clk_in			(clk_in), 
    .up_down		(up_down), 
    .tdr_reg		(tdr_reg), 
    .enable			(enable), 
    .load_tdr		(load_tdr), 
    .cnt			(cnt), 
    .s_ovf			(s_ovf), 
    .s_udf			(s_udf)
  );
endmodule

I fix the code that you mention. But I still got the errors above.
I name 5 files like timer.v, register_control.v, logic_control.v, counter.v and tb_timer.v
in questasim. I need to get the coverage for the project. Is there any chances that I get this error because of the way I initialize reg and wire?

You should have changed timer.v to timer.sv. Change the other filenames back to end with *.sv.

1 Like