fomu-workshop/verilog/blink-expanded/blink.v
Pascal Mainini ac248884fa Fix verilog examples
* Move blink-expanded to blink and vice versa (blink expanded was actually simpler)
* Remove unused code and make minimal example more minimal
* Add support for different Fomu boards to "blink"
* Update documentation
2020-01-11 17:14:15 +01:00

167 lines
5.2 KiB
Verilog

// Simple tri-colour LED blink example, with button control
//
// Green LED blinks forever, blue and red LED can be turned on by connecting
// pins or pressing buttons.
//
// On EVT Fomu boards:
//
// 1st LED colour - Blue - controlled by pressing Button 5, or connect 1 to 2
// 2nd LED colour - Red - controlled by pressing Button 6, or connect 3 to 4
// 3rd LED colour - Green - controlled by clock (blinking)
//
// On DVT / Hacker / Production Fomu boards:
//
// 1st LED colour - Blue - turn on by connecting pin 1 to pin 2
// 2nd LED colour - Green - controlled by clock (blinking)
// 3rd LED colour - Red - turn on by connecting pin 3 to pin 4
//
// We use `defines to handle these two cases, because the SB_RGBA_DRV
// iCE40UP5K hard macro is unable to do RGBn to output pin mapping internally
// (the RGB0 / RGB1 / RGB2 parameters to SB_RGBA_DRV *must* be mapped
// to the same named RGB0 / RGB1 / RGB2 physical pins; arachne-pnr
// errors if they are not, and currently nextpnr just ignores mismapped
// pins and enforces this mapping)
//
// This is all kludged into a single file to make a standalone simple test;
// a better design would wrap SB_RGBA_DRV into a Fomu specific module and
// hide the LED colour mapping; and also set the appropriate pins for
// the buttons at instantiation time.
//
`ifdef EVT
`define BLUEPWM RGB0PWM
`define REDPWM RGB1PWM
`define GREENPWM RGB2PWM
`elsif HACKER
`define BLUEPWM RGB0PWM
`define GREENPWM RGB1PWM
`define REDPWM RGB2PWM
`elsif PVT
`define GREENPWM RGB0PWM
`define REDPWM RGB1PWM
`define BLUEPWM RGB2PWM
`else
`error_board_not_supported
`endif
module blink (
// 48MHz Clock input
// --------
input clki,
// LED outputs
// --------
output rgb0,
output rgb1,
output rgb2,
// User touchable pins
// --------
// Connect 1-2 to enable blue LED
input user_1,
output user_2,
// Connect 3-4 to enable red LED
output user_3,
input user_4,
// USB Pins (which should be statically driven if not being used).
// --------
output usb_dp,
output usb_dn,
output usb_dp_pu
);
// Assign USB pins to "0" so as to disconnect Fomu from
// the host system. Otherwise it would try to talk to
// us over USB, which wouldn't work since we have no stack.
assign usb_dp = 1'b0;
assign usb_dn = 1'b0;
assign usb_dp_pu = 1'b0;
// Connect to system clock (with buffering)
wire clk;
SB_GB clk_gb (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clki),
.GLOBAL_BUFFER_OUTPUT(clk)
);
// Configure user pins so that we can detect the user connecting
// 1-2 or 3-4 with conductive material.
//
// We do this by grounding user_2 and user_3, and configuring inputs
// with pullups on user_1 and user_4.
assign user_2 = 1'b0;
assign user_3 = 1'b0;
localparam SB_IO_TYPE_SIMPLE_INPUT = 6'b000001;
wire user_1_pulled;
SB_IO #(
.PIN_TYPE(SB_IO_TYPE_SIMPLE_INPUT),
.PULLUP(1'b1)
) user_1_io (
.PACKAGE_PIN(user_1),
.OUTPUT_ENABLE(1'b0),
.INPUT_CLK(clk),
.D_IN_0(user_1_pulled),
);
wire user_4_pulled;
SB_IO #(
.PIN_TYPE(SB_IO_TYPE_SIMPLE_INPUT),
.PULLUP(1'b 1)
) user_4_io (
.PACKAGE_PIN(user_4),
.OUTPUT_ENABLE(1'b0),
.INPUT_CLK(clk),
.D_IN_0(user_4_pulled),
);
wire enable_blue = ~user_1_pulled;
wire enable_red = ~user_4_pulled;
// Use counter logic to divide system clock. The clock is 48 MHz,
// so we divide it down by 2^28.
reg [28:0] counter = 0;
always @(posedge clk) begin
counter <= counter + 1;
end
// Instantiate iCE40 LED driver hard logic, connecting up
// latched button state, counter state, and LEDs.
//
// Note that it's possible to drive the LEDs directly,
// however that is not current-limited and results in
// overvolting the red LED.
//
// See also:
// https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/IK/ICE40LEDDriverUsageGuide.ashx?document_id=50668
SB_RGBA_DRV RGBA_DRIVER (
.CURREN(1'b1),
.RGBLEDEN(1'b1),
.`BLUEPWM(enable_blue), // Blue
.`REDPWM(enable_red), // Red
.`GREENPWM(counter[23]), // Green (blinking)
.RGB0(rgb0),
.RGB1(rgb1),
.RGB2(rgb2)
);
// Parameters from iCE40 UltraPlus LED Driver Usage Guide, pages 19-20
localparam RGBA_CURRENT_MODE_FULL = "0b0";
localparam RGBA_CURRENT_MODE_HALF = "0b1";
// Current levels in Full / Half mode
localparam RGBA_CURRENT_04MA_02MA = "0b000001";
localparam RGBA_CURRENT_08MA_04MA = "0b000011";
localparam RGBA_CURRENT_12MA_06MA = "0b000111";
localparam RGBA_CURRENT_16MA_08MA = "0b001111";
localparam RGBA_CURRENT_20MA_10MA = "0b011111";
localparam RGBA_CURRENT_24MA_12MA = "0b111111";
// Set parameters of RGBA_DRIVER (output current)
//
// Mapping of RGBn to LED colours determined experimentally
defparam RGBA_DRIVER.CURRENT_MODE = RGBA_CURRENT_MODE_HALF;
defparam RGBA_DRIVER.RGB0_CURRENT = RGBA_CURRENT_16MA_08MA; // Blue - Needs more current.
defparam RGBA_DRIVER.RGB1_CURRENT = RGBA_CURRENT_08MA_04MA; // Red
defparam RGBA_DRIVER.RGB2_CURRENT = RGBA_CURRENT_08MA_04MA; // Green
endmodule