268 lines
9.8 KiB
Verilog
268 lines
9.8 KiB
Verilog
`timescale 1ns / 1ps
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Engineer: Sergiusz Bazanski <sergiusz@bazanski.pl>
|
|
// Licensed under BSD 2-Clause, see COPYING.
|
|
//
|
|
// Create Date: 11/10/2015 10:42:10 PM
|
|
// Design Name:
|
|
// Module Name: blitter
|
|
// Project Name: LED Controller for GRAET JUSTICE
|
|
// Target Devices: MYiR ZTurn Board with Zynq-7010
|
|
// Tool Versions:
|
|
// Description:
|
|
//
|
|
// Dependencies:
|
|
//
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
module blitter #(
|
|
// Number of display chains
|
|
parameter integer C_LED_CHAINS = 4,
|
|
// Maximum length of display chain
|
|
parameter integer C_LED_CHAIN_LENGTH = 4,
|
|
// Number of banks in one chain
|
|
parameter integer C_LED_NBANKS = 16,
|
|
// Width of one display
|
|
parameter integer C_LED_WIDTH = 32,
|
|
// Clock divider from system clock to LED blit clock
|
|
parameter integer C_LED_CLKDIV = 10,
|
|
// Bits per pixel colour
|
|
parameter integer C_BPC = 8
|
|
)
|
|
(
|
|
// TODO: change to target config registers
|
|
/*input [31:0] bufa_base,
|
|
input [31:0] bufa_limit,
|
|
input [31:0] bufb_base,
|
|
input [31:0] bufb_limit,
|
|
input buf_sel,*/
|
|
|
|
input sys_en,
|
|
input sys_clk,
|
|
input sys_rst,
|
|
|
|
output reg led_clk,
|
|
output reg led_stb,
|
|
output led_oe,
|
|
output [$clog2(C_LED_NBANKS)-1 : 0] led_bank
|
|
// This module is currently hardcoded to be C_LED_NBANKS * 2 high (so be '1 / C_LED_NBANKS scan')
|
|
/*output [C_LED_CHAIN_LENGTH-1 : 0] led_r0,
|
|
output [C_LED_CHAIN_LENGTH-1 : 0] led_r1,
|
|
output [C_LED_CHAIN_LENGTH-1 : 0] led_g0,
|
|
output [C_LED_CHAIN_LENGTH-1 : 0] led_g1,
|
|
output [C_LED_CHAIN_LENGTH-1 : 0] led_b0,
|
|
output [C_LED_CHAIN_LENGTH-1 : 0] led_b1,
|
|
output [C_LED_NBANKS-1 : 0] led_bank*/
|
|
);
|
|
|
|
/// Counters
|
|
// Current line pixel counter
|
|
reg [$clog2(C_LED_WIDTH * C_LED_CHAIN_LENGTH)-1 : 0] pixel_counter;
|
|
// Frame bank counter
|
|
reg [$clog2(C_LED_NBANKS)-1 : 0] bank_counter;
|
|
assign led_bank = bank_counter;
|
|
// Individual pixel divider
|
|
reg [$clog2(C_LED_CLKDIV)-1 : 0] divider_counter;
|
|
// Subframe counter (for pixel bits)
|
|
reg [$clog2(C_BPC)-1 : 0] subframe_counter;
|
|
|
|
/// Internal condition signals
|
|
// Are we blitting out the last pixel in the current line?
|
|
wire last_pixel = (pixel_counter >= (C_LED_WIDTH * C_LED_CHAIN_LENGTH) - 1);
|
|
// Are we blitting out the last line in the current subframe?
|
|
wire last_line = (bank_counter >= C_LED_NBANKS - 1);
|
|
// Are we blitting out the last subframe in the frame?
|
|
wire last_subframe = (subframe_counter >= C_BPC - 1);
|
|
|
|
/// Line FSM
|
|
// Idle
|
|
`define LINE_IDLE 3'b000
|
|
// Preparation before first pixel of line
|
|
`define LINE_PREPARE_FOR_BURST 3'b001
|
|
// Burst out a pixel, switch to next one if divider counter reaches zero
|
|
`define LINE_BURST 3'b010
|
|
// Wait for latch confirmation
|
|
`define LINE_WAIT_FOR_CONFIRMATION 3'b011
|
|
// Latch out this row,
|
|
`define LINE_LATCH 3'b100
|
|
reg [2:0] line_fsm_state;
|
|
wire line_fsm_start;
|
|
reg line_fsm_busy;
|
|
assign led_oe = line_fsm_busy;
|
|
always @(posedge sys_clk or negedge sys_rst)
|
|
begin
|
|
if (~sys_rst) begin
|
|
/// Reset counters
|
|
// New output period
|
|
divider_counter <= 0;
|
|
// New column
|
|
pixel_counter <= 0;
|
|
// New row
|
|
bank_counter <= 0;
|
|
|
|
line_fsm_busy <= 0;
|
|
line_fsm_state <= `LINE_IDLE;
|
|
end else begin
|
|
case (line_fsm_state)
|
|
`LINE_IDLE: begin
|
|
// Two-step for potential clock domain cross
|
|
if (~line_fsm_busy) begin
|
|
if (line_fsm_start) begin
|
|
line_fsm_busy <= 1;
|
|
end else begin
|
|
line_fsm_busy <= 0;
|
|
end
|
|
end else begin
|
|
line_fsm_state <= `LINE_PREPARE_FOR_BURST;
|
|
end
|
|
end
|
|
`LINE_PREPARE_FOR_BURST: begin
|
|
// Begin new pixel
|
|
divider_counter <= C_LED_CLKDIV - 1;
|
|
// Begin new row
|
|
pixel_counter <= 0;
|
|
|
|
// Set outputs
|
|
led_clk <= 0;
|
|
led_stb <= 0;
|
|
|
|
line_fsm_state <= `LINE_BURST;
|
|
end
|
|
`LINE_BURST: begin
|
|
// Count down divider_counter, increment pixel_counter on underflow
|
|
if (divider_counter == 0) begin
|
|
// Count up pixel counter, see if we blitted all pixels in this row
|
|
if (last_pixel) begin
|
|
pixel_counter <= 0;
|
|
line_fsm_state <= `LINE_LATCH;
|
|
end else begin
|
|
pixel_counter <= pixel_counter + 1;
|
|
end
|
|
divider_counter <= C_LED_CLKDIV - 1;
|
|
end else begin
|
|
divider_counter <= divider_counter - 1;
|
|
end
|
|
// Stretch LED clock accross countdown domain (so that we clock out the data in the middle of the period)
|
|
// _______ _______
|
|
// | | | |
|
|
// clk ______| |______| |____
|
|
// dat ><=============><=============><=====
|
|
// ctr 80---------> 0 80 --------> 0 80 ----
|
|
if (divider_counter < (C_LED_CLKDIV / 2))
|
|
led_clk <= 0;
|
|
else
|
|
led_clk <= 1;
|
|
end
|
|
`LINE_LATCH: begin
|
|
// Cleanup after previous state...
|
|
led_clk <= 0;
|
|
|
|
if (divider_counter == 0) begin
|
|
if (last_line) begin
|
|
line_fsm_state <= `LINE_IDLE;
|
|
line_fsm_busy <= 0;
|
|
bank_counter <= 0;
|
|
end else begin
|
|
line_fsm_state <= `LINE_PREPARE_FOR_BURST;
|
|
bank_counter <= bank_counter + 1;
|
|
end
|
|
end else begin
|
|
divider_counter <= divider_counter - 1;
|
|
end
|
|
|
|
if (divider_counter < (C_LED_CLKDIV / 2))
|
|
led_stb <= 0;
|
|
else
|
|
led_stb <= 1;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
/// Subframe FSM
|
|
`define SUBFRAME_IDLE 3'b000
|
|
`define SUBFRAME_CALIBRATE 3'b001
|
|
`define SUBFRAME_CALIBRATE2 3'b010
|
|
`define SUBFRAME_WAIT 3'b011
|
|
`define SUBFRAME_WAIT2 3'b100
|
|
reg [2:0] subframe_fsm_state;
|
|
|
|
// Create some register for Line FSM control lines
|
|
reg subframe_line_start;
|
|
assign line_fsm_start = subframe_line_start;
|
|
|
|
// Delay counter for subframe, and calibration counter (for first, smaller interval)
|
|
// We need to do some calculations... this should be able to fit
|
|
// 2**C_BPC * (C_LED_WIDTH * C_LED_CHAIN_LENGTH) * C_LED_CLKDIV * C_LED_NBANKS * 2
|
|
// | | | | \____ convservative guesstimate :v
|
|
// | | | \____ bank count
|
|
// | | \____ amounts of sysclocks we spend on a pixel
|
|
// | \___ number of LEDs we drive in one line
|
|
// \___ bits per pixel timeslot change (if shortest period is X, longest is X * 2**C_BPC)
|
|
reg [$clog2(2**C_BPC * (C_LED_WIDTH * C_LED_CHAIN_LENGTH) * C_LED_CLKDIV * C_LED_NBANKS * 2)-1: 0] subframe_delay;
|
|
reg [$clog2((C_LED_WIDTH * C_LED_CHAIN_LENGTH) * C_LED_CLKDIV * C_LED_NBANKS * 2)-1: 0] subframe_calibration_delay;
|
|
|
|
always @(posedge sys_clk or negedge sys_rst)
|
|
begin
|
|
if (~sys_rst) begin
|
|
subframe_counter <= 0;
|
|
subframe_calibration_delay <= 0;
|
|
subframe_delay <= 0;
|
|
|
|
subframe_line_start <= 0;
|
|
subframe_fsm_state <= `SUBFRAME_IDLE;
|
|
end else begin
|
|
case (subframe_fsm_state)
|
|
`SUBFRAME_IDLE: begin
|
|
if (!line_fsm_busy) begin
|
|
subframe_line_start <= 1;
|
|
if (subframe_counter == 0) begin
|
|
// we're running the first, smallest subframe interval - start calibration
|
|
subframe_fsm_state <= `SUBFRAME_CALIBRATE;
|
|
subframe_delay <= 0;
|
|
end else begin
|
|
subframe_fsm_state <= `SUBFRAME_WAIT;
|
|
subframe_delay <= (subframe_calibration_delay << subframe_counter);
|
|
end
|
|
end else begin
|
|
end
|
|
end
|
|
`SUBFRAME_CALIBRATE: begin
|
|
subframe_line_start <= 0;
|
|
subframe_calibration_delay <= 0;
|
|
subframe_fsm_state <= `SUBFRAME_CALIBRATE2;
|
|
end
|
|
`SUBFRAME_CALIBRATE2: begin
|
|
if (!line_fsm_busy) begin
|
|
subframe_calibration_delay <= (subframe_calibration_delay >> 1);
|
|
subframe_delay <= ((subframe_calibration_delay>>1) << subframe_counter);
|
|
subframe_fsm_state <= `SUBFRAME_WAIT2;
|
|
end else begin
|
|
subframe_calibration_delay <= subframe_calibration_delay + 1;
|
|
end
|
|
end
|
|
`SUBFRAME_WAIT: begin
|
|
subframe_line_start <= 0;
|
|
subframe_fsm_state <= `SUBFRAME_WAIT2;
|
|
end
|
|
`SUBFRAME_WAIT2: begin
|
|
if (!line_fsm_busy) begin
|
|
if (subframe_delay == 0) begin
|
|
subframe_fsm_state <= `SUBFRAME_IDLE;
|
|
|
|
if (last_line) begin
|
|
subframe_counter <= 0;
|
|
end else begin
|
|
subframe_counter <= subframe_counter + 1;
|
|
end
|
|
end else begin
|
|
subframe_delay <= subframe_delay - 1;
|
|
end
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
endmodule
|