Permalink
Browse files

LED matrix controller

  • Loading branch information...
wpwrak committed Jan 26, 2012
1 parent 530ae74 commit 1ec6828bbdb58a5b6cbbf8a47f281198ad9a9439
@@ -28,7 +28,7 @@
`define ENABLE_AC97
`define ENABLE_PFPU
`define ENABLE_TMU
-`define ENABLE_FMLMETER
+/*`define ENABLE_FMLMETER*/
`define ENABLE_VIDEOIN
`define ENABLE_MIDI
`define ENABLE_DMX
@@ -137,6 +137,10 @@ module system(
// Expansion connector
input [11:0] exp,
+ // LED matrix
+ inout [2:0] ledr,
+ inout [3:0] ledc,
+
// PCB revision
input [3:0] pcb_revision
);
@@ -508,6 +512,7 @@ wire [31:0] csr_dr_uart,
csr_dr_tmu,
csr_dr_ethernet,
csr_dr_fmlmeter,
+ csr_dr_ledm,
csr_dr_videoin,
csr_dr_midi,
csr_dr_dmx_tx,
@@ -670,6 +675,7 @@ csrbrg csrbrg(
|csr_dr_tmu
|csr_dr_ethernet
|csr_dr_fmlmeter
+ |csr_dr_ledm
|csr_dr_videoin
|csr_dr_midi
|csr_dr_dmx_tx
@@ -1338,8 +1344,29 @@ fmlmeter #(
.fml_we(fml_we),
.fml_adr(fml_adr)
);
+assign csr_dr_ledm = 32'd0;
`else
+//---------------------------------------------------------------------------
+// LED matrix
+//---------------------------------------------------------------------------
assign csr_dr_fmlmeter = 32'd0;
+
+ledm #(
+ .csr_addr(4'h9),
+ .clk_freq(`CLOCK_FREQUENCY)
+) ledm (
+ .sys_clk(sys_clk),
+ .sys_rst(sys_rst),
+
+ .csr_a(csr_a),
+ .csr_we(csr_we),
+ .csr_di(csr_dw),
+ .csr_do(csr_dr_ledm),
+
+ .ledr(ledr),
+ .ledc(ledc)
+);
+
`endif
//---------------------------------------------------------------------------
@@ -43,5 +43,11 @@ DMX_SRC=$(wildcard $(CORES_DIR)/dmx/rtl/*.v)
USB_SRC=$(wildcard $(CORES_DIR)/softusb/rtl/*.v)
MEMTEST_SRC=$(wildcard $(CORES_DIR)/memtest/rtl/*.v)
MONITOR_SRC=$(wildcard $(CORES_DIR)/monitor/rtl/*.v)
+LEDM_SRC=$(wildcard $(CORES_DIR)/ledm/rtl/*.v)
-CORES_SRC=$(ASFIFO_SRC) $(CONBUS_SRC) $(LM32_SRC) $(FMLARB_SRC) $(FMLBRG_SRC) $(CSRBRG_SRC) $(NORFLASH_SRC) $(UART_SRC) $(SYSCTL_SRC) $(HPDMC_SRC) $(VGAFB_SRC) $(MEMCARD_SRC) $(AC97_SRC) $(PFPU_SRC) $(TMU_SRC) $(ETHERNET_SRC) $(FMLMETER_SRC) $(VIDEOIN_SRC) $(DMX_SRC) $(IR_SRC) $(USB_SRC) $(MEMTEST_SRC) $(MONITOR_SRC)
+CORES_SRC=$(ASFIFO_SRC) $(CONBUS_SRC) $(LM32_SRC) $(FMLARB_SRC) $(FMLBRG_SRC) \
+ $(CSRBRG_SRC) $(NORFLASH_SRC) $(UART_SRC) $(SYSCTL_SRC) \
+ $(HPDMC_SRC) $(VGAFB_SRC) $(MEMCARD_SRC) $(AC97_SRC) $(PFPU_SRC) \
+ $(TMU_SRC) $(ETHERNET_SRC) $(FMLMETER_SRC) $(VIDEOIN_SRC) \
+ $(DMX_SRC) $(IR_SRC) $(USB_SRC) $(MEMTEST_SRC) $(MONITOR_SRC) \
+ $(LEDM_SRC)
@@ -297,23 +297,31 @@ NET "ir_rx" LOC = C16 | IOSTANDARD = LVCMOS33;
# ==== Expansion connector ====
NET "exp(0)" LOC = A20;
-NET "exp(1)" LOC = F16;
+#NET "exp(1)" LOC = F16;
NET "exp(2)" LOC = A21;
-NET "exp(3)" LOC = F17;
-NET "exp(4)" LOC = B21;
-NET "exp(5)" LOC = H16;
-NET "exp(6)" LOC = B22;
+#NET "exp(3)" LOC = F17;
+#NET "exp(4)" LOC = B21;
+#NET "exp(5)" LOC = H16;
+#NET "exp(6)" LOC = B22;
NET "exp(7)" LOC = H17;
-NET "exp(8)" LOC = G16;
+#NET "exp(8)" LOC = G16;
NET "exp(9)" LOC = J16;
-NET "exp(10)" LOC = G17;
+#NET "exp(10)" LOC = G17;
NET "exp(11)" LOC = K16;
NET "exp(*)" IOSTANDARD = LVCMOS33;
# avoid floating signals
NET "exp(*)" PULLDOWN;
+NET "ledr(0)" LOC = H16 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 24;
+NET "ledr(1)" LOC = F17 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 24;
+NET "ledr(2)" LOC = F16 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 24;
+NET "ledc(0)" LOC = B21 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 6;
+NET "ledc(1)" LOC = B22 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 6;
+NET "ledc(2)" LOC = G16 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 6;
+NET "ledc(3)" LOC = G17 | IOSTANDARD = LVCMOS33 | SLEW = QUIETIO | DRIVE = 6;
+
# ==== PCB revision ====
NET "pcb_revision(0)" LOC = AA10;
NET "pcb_revision(1)" LOC = AB10;
View
@@ -0,0 +1,173 @@
+/*
+ * ledm.v - LED matrix controller
+ *
+ * Copyright 2012 by Werner Almesberger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License.
+ */
+
+/*
+ * LEDs are organized as antiparallel pairs in a matrix of R rows and C
+ * columns. The controller cycles through the rows and lights first the
+ * LEDs in one half of the pairs and then the ones in the other half.
+ *
+ * Inactive rows and columns are driven to Z.
+ *
+ * LED brightness is controlled by an R*C*2-channel PWM controller with
+ * common period and per-channel duty cycle. A value of 0 disables the
+ * LED.
+ */
+
+module ledm #(
+ parameter csr_addr = 4'h9,
+ parameter clk_freq = 100000000, /* 100 MHz */
+ parameter refresh = 1000, /* 1 kHz */
+ parameter rows = 3,
+ parameter cols = 4,
+ parameter row_bits = 2,
+ parameter col_bits = 2,
+ parameter pwm_bits = 8,
+ parameter prescaler_bits = 8
+) (
+ input sys_clk,
+ input sys_rst,
+
+ input [13:0] csr_a,
+ input csr_we,
+ input [31:0] csr_di,
+ output reg [31:0] csr_do,
+
+ inout [rows-1:0] ledr,
+ inout [cols-1:0] ledc
+);
+
+wire csr_selected = csr_a[13:10] == csr_addr;
+
+/*
+ * LED register array (with default parameters):
+ * row (3) * column (4) * direction (2)
+ *
+ * Register address:
+ * 9 8 7 6 5 4 3 2 1 0
+ * x 0 x x x R R C C D LED at row R, column C, direction D
+ * x 1 x x x x x x x x prescaler
+ *
+ */
+
+reg [pwm_bits-1:0] led[0:rows-1][0:1][0:cols-1];
+ /* PWM end; [row] [direction] [column] */
+reg [prescaler_bits-1:0] prescaler; /* prescaler cycle */
+
+reg [rows-1:0] ledr_reg; /* row output buffer */
+reg [cols-1:0] ledc_reg; /* column output buffer */
+assign ledr = ledr_reg;
+assign ledc = ledc_reg;
+
+reg [prescaler_bits-1:0] pre_count; /* prescaler, 0 ... prescaler-1 */
+reg [pwm_bits-1:0] pwm_count; /* PWM, 0 ... (1 << pwm_bits)-1 */
+reg [row_bits-1:0] row, curr_row; /* row counter and cached current row */
+reg dir; /* direction toggle */
+
+`define REG_ACCESS \
+ led[csr_a[col_bits+row_bits:col_bits+1]] \
+ [csr_a[0]] \
+ [csr_a[col_bits:1]]
+
+assign ps_sel = csr_a[8];
+
+
+always @(posedge sys_clk) begin: _
+ if (sys_rst) begin
+ initialize();
+ disable _;
+ end
+
+ csr_do <= 0;
+ if (csr_selected) begin
+ if (!ps_sel &&
+ csr_a[col_bits+row_bits:col_bits+1] <= rows &&
+ csr_a[col_bits:1] <= cols) begin
+ csr_do <= `REG_ACCESS;
+ if (csr_we)
+ `REG_ACCESS <= csr_di[pwm_bits-1:0];
+ end
+ if (ps_sel) begin
+ csr_do <= prescaler;
+ if (csr_we)
+ prescaler <= csr_di[prescaler_bits-1:0];
+ end
+ end
+
+ if (pre_count < prescaler) begin
+ pre_count <= pre_count+1'd1;
+ end else begin
+ pre_count <= 0;
+ if (pwm_count)
+ tick();
+ else
+ setup_row();
+ pwm_count <= pwm_count+1'd1;
+ end
+end
+
+
+task initialize;
+begin: _
+ integer r, d, c;
+
+ /*
+ * System clock / PWM steps / directions / rows / refresh
+ * 80 MHz / 256 / 2 / 3 / 1 kHz = 52
+ */
+ prescaler <= (clk_freq >> pwm_bits)/2/rows/refresh;
+ pre_count <= 0;
+ pwm_count <= 0;
+ row <= 0;
+ curr_row <= 0;
+ dir <= 0;
+
+ for (r = 0; r != rows; r = r+1)
+ ledr_reg[r] <= 1'bz;
+ for (c = 0; c != cols; c = c+1)
+ ledc_reg[c] <= 1'bz;
+ for (r = 0; r != rows; r = r+1)
+ for (d = 0; d != 2; d = d+1)
+ for (c = 0; c != cols; c = c+1)
+ led[r][d][c] <= 0;
+end
+endtask
+
+
+task tick;
+begin: _
+ integer i;
+
+ for (i = 0; i != cols; i = i+1)
+ if (led[curr_row][dir][i] == pwm_count)
+ ledc_reg[i] <= 1'bz;
+end
+endtask
+
+
+task setup_row;
+begin: _
+ integer i;
+
+ for (i = 0; i != rows; i = i+1)
+ ledr_reg[i] <= row == i ? dir : 1'bz;
+ for (i = 0; i != cols; i = i+1)
+ ledc_reg[i] <= led[row][!dir][i] ? !dir : 1'bz;
+ curr_row <= row;
+ if (dir) begin
+ if (row == rows-1)
+ row <= 0;
+ else
+ row <= row+1'd1;
+ end
+ dir <= !dir;
+end
+endtask
+
+endmodule

0 comments on commit 1ec6828

Please sign in to comment.