blob: 1e5dfa868f316b09f069a1534c181d5caa602120 [file] [log] [blame]
module tinyfpga_bootloader (
input clk_48mhz,
input reset,
// USB lines. Split into input vs. output and oe control signal to maintain
// highest level of compatibility with synthesis tools.
output usb_p_tx,
output usb_n_tx,
input usb_p_rx,
input usb_n_rx,
output usb_tx_en,
// bootloader indicator light, pulses on and off when bootloader is active
output led,
// connection to SPI flash
output spi_cs,
output spi_sck,
output spi_mosi,
input spi_miso,
// when asserted it indicates the bootloader is ready for the FPGA to load
// the user config. different FPGAs use different primitives for this
// function.
output boot
);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////
//////// bootloader LED
////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
reg [7:0] led_pwm = 0;
reg [7:0] pwm_cnt = 0;
reg [5:0] ns_cnt = 0;
wire ns_rst = (ns_cnt == 48);
always @(posedge clk_48mhz) begin
if (ns_rst) begin
ns_cnt <= 0;
end else begin
ns_cnt <= ns_cnt + 1'b1;
end
end
reg [9:0] us_cnt = 0;
wire us_rst = (us_cnt == 1000);
always @(posedge clk_48mhz) begin
if (us_rst) begin
us_cnt <= 0;
end else if (ns_rst) begin
us_cnt <= us_cnt + 1'b1;
end
end
reg count_down = 0;
always @(posedge clk_48mhz) begin
if (us_rst) begin
if (count_down) begin
if (led_pwm == 0) begin
count_down <= 0;
end else begin
led_pwm <= led_pwm - 1'b1;
end
end else begin
if (led_pwm == 255) begin
count_down <= 1;
end else begin
led_pwm <= led_pwm + 1'b1;
end
end
end
end
always @(posedge clk_48mhz) pwm_cnt <= pwm_cnt + 1'b1;
assign led = led_pwm > pwm_cnt;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////
//////// usb engine
////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
wire [6:0] dev_addr;
wire [7:0] out_ep_data;
wire ctrl_out_ep_req;
wire ctrl_out_ep_grant;
wire ctrl_out_ep_data_avail;
wire ctrl_out_ep_setup;
wire ctrl_out_ep_data_get;
wire ctrl_out_ep_stall;
wire ctrl_out_ep_acked;
wire ctrl_in_ep_req;
wire ctrl_in_ep_grant;
wire ctrl_in_ep_data_free;
wire ctrl_in_ep_data_put;
wire [7:0] ctrl_in_ep_data;
wire ctrl_in_ep_data_done;
wire ctrl_in_ep_stall;
wire ctrl_in_ep_acked;
wire serial_out_ep_req;
wire serial_out_ep_grant;
wire serial_out_ep_data_avail;
wire serial_out_ep_setup;
wire serial_out_ep_data_get;
wire serial_out_ep_stall;
wire serial_out_ep_acked;
wire serial_in_ep_req;
wire serial_in_ep_grant;
wire serial_in_ep_data_free;
wire serial_in_ep_data_put;
wire [7:0] serial_in_ep_data;
wire serial_in_ep_data_done;
wire serial_in_ep_stall;
wire serial_in_ep_acked;
wire sof_valid;
wire [10:0] frame_index;
reg [31:0] host_presence_timer = 0;
reg host_presence_timeout = 0;
wire boot_to_user_design;
assign boot = host_presence_timeout || boot_to_user_design;
usb_serial_ctrl_ep ctrl_ep_inst (
.clk(clk_48mhz),
.reset(reset),
.dev_addr(dev_addr),
// out endpoint interface
.out_ep_req(ctrl_out_ep_req),
.out_ep_grant(ctrl_out_ep_grant),
.out_ep_data_avail(ctrl_out_ep_data_avail),
.out_ep_setup(ctrl_out_ep_setup),
.out_ep_data_get(ctrl_out_ep_data_get),
.out_ep_data(out_ep_data),
.out_ep_stall(ctrl_out_ep_stall),
.out_ep_acked(ctrl_out_ep_acked),
// in endpoint interface
.in_ep_req(ctrl_in_ep_req),
.in_ep_grant(ctrl_in_ep_grant),
.in_ep_data_free(ctrl_in_ep_data_free),
.in_ep_data_put(ctrl_in_ep_data_put),
.in_ep_data(ctrl_in_ep_data),
.in_ep_data_done(ctrl_in_ep_data_done),
.in_ep_stall(ctrl_in_ep_stall),
.in_ep_acked(ctrl_in_ep_acked)
);
usb_spi_bridge_ep usb_spi_bridge_ep_inst (
.clk(clk_48mhz),
.reset(reset),
// out endpoint interface
.out_ep_req(serial_out_ep_req),
.out_ep_grant(serial_out_ep_grant),
.out_ep_data_avail(serial_out_ep_data_avail),
.out_ep_setup(serial_out_ep_setup),
.out_ep_data_get(serial_out_ep_data_get),
.out_ep_data(out_ep_data),
.out_ep_stall(serial_out_ep_stall),
.out_ep_acked(serial_out_ep_acked),
// in endpoint interface
.in_ep_req(serial_in_ep_req),
.in_ep_grant(serial_in_ep_grant),
.in_ep_data_free(serial_in_ep_data_free),
.in_ep_data_put(serial_in_ep_data_put),
.in_ep_data(serial_in_ep_data),
.in_ep_data_done(serial_in_ep_data_done),
.in_ep_stall(serial_in_ep_stall),
.in_ep_acked(serial_in_ep_acked),
// spi interface
.spi_cs_b(spi_cs),
.spi_sck(spi_sck),
.spi_mosi(spi_mosi),
.spi_miso(spi_miso),
// warm boot interface
.boot_to_user_design(boot_to_user_design)
);
wire nak_in_ep_grant;
wire nak_in_ep_data_free;
wire nak_in_ep_acked;
usb_fs_pe #(
.NUM_OUT_EPS(5'd2),
.NUM_IN_EPS(5'd3)
) usb_fs_pe_inst (
.clk(clk_48mhz),
.reset(reset),
.usb_p_tx(usb_p_tx),
.usb_n_tx(usb_n_tx),
.usb_p_rx(usb_p_rx),
.usb_n_rx(usb_n_rx),
.usb_tx_en(usb_tx_en),
.dev_addr(dev_addr),
// out endpoint interfaces
.out_ep_req({serial_out_ep_req, ctrl_out_ep_req}),
.out_ep_grant({serial_out_ep_grant, ctrl_out_ep_grant}),
.out_ep_data_avail({serial_out_ep_data_avail, ctrl_out_ep_data_avail}),
.out_ep_setup({serial_out_ep_setup, ctrl_out_ep_setup}),
.out_ep_data_get({serial_out_ep_data_get, ctrl_out_ep_data_get}),
.out_ep_data(out_ep_data),
.out_ep_stall({serial_out_ep_stall, ctrl_out_ep_stall}),
.out_ep_acked({serial_out_ep_acked, ctrl_out_ep_acked}),
// in endpoint interfaces
.in_ep_req({1'b0, serial_in_ep_req, ctrl_in_ep_req}),
.in_ep_grant({nak_in_ep_grant, serial_in_ep_grant, ctrl_in_ep_grant}),
.in_ep_data_free({nak_in_ep_data_free, serial_in_ep_data_free, ctrl_in_ep_data_free}),
.in_ep_data_put({1'b0, serial_in_ep_data_put, ctrl_in_ep_data_put}),
.in_ep_data({8'b0, serial_in_ep_data[7:0], ctrl_in_ep_data[7:0]}),
.in_ep_data_done({1'b0, serial_in_ep_data_done, ctrl_in_ep_data_done}),
.in_ep_stall({1'b0, serial_in_ep_stall, ctrl_in_ep_stall}),
.in_ep_acked({nak_in_ep_acked, serial_in_ep_acked, ctrl_in_ep_acked}),
// sof interface
.sof_valid(sof_valid),
.frame_index(frame_index)
);
////////////////////////////////////////////////////////////////////////////////
// host presence detection
////////////////////////////////////////////////////////////////////////////////
always @(posedge clk_48mhz) begin
if (sof_valid) begin
host_presence_timer <= 0;
host_presence_timeout <= 0;
end else begin
host_presence_timer <= host_presence_timer + 1;
end
if (host_presence_timer > 48000000) begin
host_presence_timeout <= 1;
end
end
endmodule