0

New to verilog here.

The Ice40 has an RGB led driver that can also be assigned as a normal IO.

Trying to access the pin without setting it as IO will give this error one IceCube2

Error during constrained IO placement E2792: Instance ipInertedIOPad_LED_B incorrectly constrained at SB_IO_OD location

To do so, the following verilog is used:

    SB_IO_OD #(             // open drain IP instance
    .PIN_TYPE(6'b011001)  // configure as output
    ) pin_out_driver (
    .PACKAGEPIN(LED_B), // connect to this pin
    .DOUT0(ledb)           // output the state of "led"
    );

So the final verilog code looks like (simplified to 1 led):

module top (output LED_R, output LED_G, output LED_B);

    reg ledb = 0;

    reg [3:0] cnt;

    SB_IO_OD #(             // open drain IP instance
    .PIN_TYPE(6'b011001)  // configure as output
    ) pin_out_driver (
    .PACKAGEPIN(LED_B), // connect to this pin
    .DOUT0(ledb)           // output the state of "led"
    );

    always
    begin
        ledb = ~cnt[2];
    end

    always @(posedge clkout)
    begin
        cnt <= cnt + 1;
    end

endmodule

and it does work.

However, I assign the led in an always block, which is sequential as I understand. Would the "correct" way to assign ledb as a wire, but then it is not compatible with the SB_IO_OD.

Is this the correct way to do it, or there is a non-sequential way to assign the led, basically not assigning in the always block.

Damien
  • 1,492
  • 10
  • 32
  • 1
    always block which assigns to *ledb* is not sequential. It makes no sense there at all because it does not have *any* sensitivity list. As minimum you should add `@*` there. It will be combinational in this case. The only sequential block is the one where you increment the counter. However, you did no bother resetting it to any useful value, so it will not work. In any case, you should start with simulation before any fpga work. – Serge Feb 07 '22 at 00:45
  • @Serge thanks for the feedback, I understand what you mean, however this actually works (maybe it shouldn't). I've seen in several tutorials that the always block is executed sequentially, also here is a reference: https://www.chipverify.com/verilog/verilog-sequential-logic-always perhaps I misunderstand? – Damien Feb 07 '22 at 01:01
  • I think you misunderstood. The code inside begin-end within an always block is executed sequentially. The order of execution across multiple always blocks is not defined. In you reference always block without sensitivity list was used in a special way within a test bench. In your answer use of *assign* was correct. Also, there is a thing called sequential logic, which describes order of execution across multiple clock cycles. – Serge Feb 07 '22 at 03:10

1 Answers1

0

After some digging, I believe this is the correct way. It also uses much less logic cells

Working code on IceCube2, simple LED blinker:

module top (output LED_R, output LED_G, output LED_B);

wire [2:0] leds;
reg [2:0] cnt;

SB_IO_OD #(             // open drain IP instance
.PIN_TYPE(6'b011001)  // configure as output
) pin_out_driver (
.PACKAGEPIN(LED_B), // connect to this pin
.DOUT0(leds[0])           // output the state of "led"
);

SB_IO_OD #(             // open drain IP instance
.PIN_TYPE(6'b011001)  // configure as output
) pin_out_driver2 (
.PACKAGEPIN(LED_G), // connect to this pin
.DOUT0(leds[1])           // output the state of "led"
);

SB_IO_OD #(             // open drain IP instance
.PIN_TYPE(6'b011001)  // configure as output
) pin_out_driver3 (
.PACKAGEPIN(LED_R), // connect to this pin
.DOUT0(leds[2])           // output the state of "led"
);

wire sysclk;

SB_HFOSC #(.CLKHF_DIV("0b00")) osc (
.CLKHFEN(1'b1),
.CLKHFPU(1'b1),
.CLKHF(sysclk) 
) /* synthesis ROUTE_THROUGH_FABRIC = 0 */;

wire clkout;
clockdivider #(.bits(28)) div(
.clkin(sysclk),
.clkout(clkout),
.div(28'd48000000)
);

assign leds  = {~cnt[2:0]};

always @(posedge clkout)
begin
    cnt <= cnt + 1;
end

endmodule
Damien
  • 1,492
  • 10
  • 32