// GNU GPL LED Display -- Display GNU GPL on LED Matrix (32x16)
//
// Copyright (C) 2006  The Free Software Initiative of Japan (FSIJ)
// This file can be distributed under the terms of GNU GPL version 2 (or later)
//
// Author: Niibe Yutaka  <gniibe@fsij.org>

module divider(clk, div_clk);
   input clk;
   output div_clk;
   reg [3:0] count;

   // When system clock is 10MHz, count[3] will be 625kHz, pulse width = 1.6us
   assign     div_clk = count[3]; // 1.60us : V = ~305Hz, 2048*1.60=3276.8us, 
   always @ (negedge clk)
     count <= count + 1;
endmodule

//
//              0 1 2 3
//<ROW><COL>:  <      ><      ><
//       BIT:  <R ><W ><R ><W ><
//     L-BIT:   ><      ><      >
//    LL-BIT:  <      ><      >< 
//       WE#:  ~~~~~~__~~~~~~__
//
//
//     S-ROW:  ~~      ~~      ~~
//     S-OUT:   ><      ><      >         = L-BIT
//     S-CLK:  ~~____~~~~____~~~~
//
//
//   BIT-OUT:  <><****><><****><>         *: valid
module matrix_driver(clk, s, row, bit_in, bit_out, 
		     opcode, oprand, 
		     s_clk, s_row, s_bit, s_latch, s_strobe);
   input            clk;
   output reg [6:0] s;		// 4 CLK * 32 columns : 128 CLK
   output reg [3:0] row;

   input 	    bit_in;
   output 	    bit_out;

   input 	    opcode;
   input [15:0]     oprand;

   output 	    s_clk, s_row, s_bit, s_latch, s_strobe;
   
   wire [4:0] 	    col;

   reg 		    l_bit, ll_bit;

   assign 	    s_clk = ~(s[0]^s[1]);
   assign 	    col =  s[6:2];
   assign 	    bit_out = (opcode==0)? l_bit :
			      (col == 0)? oprand[row] : ll_bit;
   assign 	    s_row = s[1:0] == 2'b00 ? 1'b1 : (col[3:0] == row);
   assign 	    s_bit = l_bit;
   assign 	    s_latch = |s[6:1];
   assign 	    s_strobe = (~s[2])|s[1];  // 75% power save

   always @ (negedge clk)
     case (s[1:0])
       2'b00: ll_bit <= l_bit;
       2'b01:  l_bit <= bit_in;
       2'b10: /* No action */;
       2'b11: /* No action */;
     endcase

   always @ (negedge clk)
     begin
	s = s + 1;		// Not <= , but = !!
	if (|s == 0)
	  row <= row + 1;
     end
endmodule

// State definition
`define DATA0 6'b000000
`define DATA1 6'b000001
`define DATA2 6'b000010
`define DATA3 6'b000011
`define DATA4 6'b000100
`define DATA5 6'b000101
`define DATA6 6'b000110
`define DATA7 6'b000111
`define DATA8 6'b001000
`define DATA9 6'b001001
`define DATAa 6'b001010
`define DATAb 6'b001011
`define DATAc 6'b001100
`define DATAd 6'b001101
`define DATAe 6'b001110
`define DATAf 6'b001111
//
`define ADDR0 6'b010000
`define ADDR1 6'b010001
`define ADDR2 6'b010010
`define ADDR3 6'b010011
`define ADDR4 6'b010100
`define ADDR5 6'b010101
`define ADDR6 6'b010110
`define ADDR7 6'b010111
`define ADDR8 6'b011000
`define ADDR9 6'b011001
`define ADDRa 6'b011010
`define ADDRb 6'b011011
`define ADDRc 6'b011100
`define ADDRd 6'b011101
`define ADDRe 6'b011110
`define ADDRf 6'b011111
//
`define START 6'b100000
`define ZERO  6'b100001
`define ONE   6'b100010
`define ACK   6'b100011
`define STOP  6'b100100
`define IDLE  6'b100101

`define CONTENT_SIZE 16'H_C4E0
module i2c_srom_fetch(clk,s,row,scl_pin,sda_pin,opcode,oprand);
   input             clk;     // 128*16 CLK for V, 8*V for one fetch
   input [6:0]       s;
   input [3:0] 	     row;
   inout 	     scl_pin, sda_pin;

   output  	     opcode;
   output [15:0]     oprand;

   reg 		     scl, sda;
   reg [17:0] 	     frame;

   reg [5:0] 	     command;
   wire [1:0] 	     state = s[1:0];
   reg 		     ack;
   reg [15:0] 	     data;

   assign 	     sda_pin = sda ? 1'bZ : 0;
   assign 	     scl_pin = scl ? 1'bZ : 0;
   assign 	     oprand = data ;
   assign 	     opcode = &frame[2:0]; // 7/8 idle, 1/8 work

   wire [15:0] 	     pc = {frame[17:3], 1'b0};


   always @(negedge row[3])
     begin
	frame = frame + 1;
	if (pc >= `CONTENT_SIZE) frame <= 0;
     end

   always @ (posedge clk)
     begin
	if ((|frame[2:0]) | (|row[3:1]))
	  command = `IDLE;
	else
	  case ({row[0], s[6:2]})
	     0: command = `START;
	     1: command = `ONE;  // DEVICE_ADDRESS6
	     2: command = `ZERO; // DEVICE_ADDRESS5
	     3: command = `ONE;  // DEVICE_ADDRESS4
	     4: command = `ZERO; // DEVICE_ADDRESS3
	     5: command = `ZERO; // DEVICE_ADDRESS2
	     6: command = `ZERO; // DEVICE_ADDRESS1
	     7: command = `ZERO; // DEVICE_ADDRESS0
	     8: command = `ZERO; // WRITE
	     9: command = `ACK;
	    10: if (ack) command = `ADDRf; else command = `STOP;
	    11: if (ack) command = `ADDRe; else command = `IDLE;
	    12: if (ack) command = `ADDRd; else command = `IDLE;
	    13: if (ack) command = `ADDRc; else command = `IDLE;
	    14: if (ack) command = `ADDRb; else command = `IDLE;
	    15: if (ack) command = `ADDRa; else command = `IDLE;
	    16: if (ack) command = `ADDR9; else command = `IDLE;
	    17: if (ack) command = `ADDR8; else command = `IDLE;
	    18: if (ack) command = `ACK;   else command = `IDLE;
	    19: if (ack) command = `ADDR7; else command = `IDLE;
	    20: if (ack) command = `ADDR6; else command = `IDLE;
	    21: if (ack) command = `ADDR5; else command = `IDLE;
	    22: if (ack) command = `ADDR4; else command = `IDLE;
	    23: if (ack) command = `ADDR3; else command = `IDLE;
	    24: if (ack) command = `ADDR2; else command = `IDLE;
	    25: if (ack) command = `ADDR1; else command = `IDLE;
	    26: if (ack) command = `ADDR0; else command = `IDLE;
	    27: if (ack) command = `ACK;   else command = `IDLE;
	    28: if (ack) command = `START; else command = `IDLE;
	    29: if (ack) command = `ONE;   else command = `IDLE;
	    30: if (ack) command = `ZERO;  else command = `IDLE;
	    31: if (ack) command = `ONE;   else command = `IDLE;
	    32: if (ack) command = `ZERO;  else command = `IDLE;
	    33: if (ack) command = `ZERO;  else command = `IDLE;
	    34: if (ack) command = `ZERO;  else command = `IDLE;
	    35: if (ack) command = `ZERO;  else command = `IDLE;
	    36: if (ack) command = `ONE;   else command = `IDLE;
	    37: if (ack) command = `ACK;   else command = `IDLE;
	    38: if (ack) command = `DATAf; else command = `STOP;
	    39: if (ack) command = `DATAe; else command = `IDLE;
	    40: if (ack) command = `DATAd; else command = `IDLE;
	    41: if (ack) command = `DATAc; else command = `IDLE;
	    42: if (ack) command = `DATAb; else command = `IDLE;
	    43: if (ack) command = `DATAa; else command = `IDLE;
	    44: if (ack) command = `DATA9; else command = `IDLE;
	    45: if (ack) command = `DATA8; else command = `IDLE;
	    46: if (ack) command = `ZERO;  else command = `IDLE; // ACK
	    47: if (ack) command = `DATA7; else command = `IDLE;
	    48: if (ack) command = `DATA6; else command = `IDLE;
	    49: if (ack) command = `DATA5; else command = `IDLE;
	    50: if (ack) command = `DATA4; else command = `IDLE;
	    51: if (ack) command = `DATA3; else command = `IDLE;
	    52: if (ack) command = `DATA2; else command = `IDLE;
	    53: if (ack) command = `DATA1; else command = `IDLE;
	    54: if (ack) command = `DATA0; else command = `IDLE;
	    55: if (ack) command = `ONE;   else command = `IDLE; // NOACK
	    56: if (ack) command = `STOP;  else command = `IDLE;
	    default:  command = `IDLE;
	  endcase
	case (command)
	  `START:
	    case (state)
	      2'b00: begin /* scl nc */ sda <= 1'b1; end // nc: no change
	      2'b01: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b10: begin scl <= 1'b1; sda <= 1'b0; end
	      2'b11: begin scl <= 1'b0; sda <= 1'b0; end
	    endcase
	  `ONE:
	    case (state)
	      2'b00: begin scl <= 1'b0; sda <= 1'b1; end
	      2'b01: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b10: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b11: begin scl <= 1'b0; sda <= 1'b1; end
	    endcase
	  `ZERO:
	    case (state)
	      2'b00: begin scl <= 1'b0; sda <= 1'b0; end
	      2'b01: begin scl <= 1'b1; sda <= 1'b0; end
	      2'b10: begin scl <= 1'b1; sda <= 1'b0; end
	      2'b11: begin scl <= 1'b0; sda <= 1'b0; end
	    endcase
	  `ACK:
	    case (state)
	      2'b00: begin scl <= 1'b0; sda <= 1'b1; end
	      2'b01: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b10: begin scl <= 1'b1; sda <= 1'b1; ack <= (sda_pin == 1'b0); end
	      2'b11: begin scl <= 1'b0; sda <= 1'b1; end
	    endcase
	  `STOP:
	    case (state)
	      2'b00: begin scl <= 1'b0; sda <= 1'b0; end
	      2'b01: begin scl <= 1'b1; sda <= 1'b0; end
	      2'b10: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b11: begin scl <= 1'b1; sda <= 1'b1; end
	    endcase
	  `IDLE:
	    case (state)
	      2'b00: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b01: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b10: begin scl <= 1'b1; sda <= 1'b1; end
	      2'b11: begin scl <= 1'b1; sda <= 1'b1; end
	    endcase
	  `DATA0,`DATA1,`DATA2,`DATA3,`DATA4,`DATA5,`DATA6,`DATA7,
	    `DATA8,`DATA9,`DATAa,`DATAb,`DATAc,`DATAd,`DATAe,`DATAf:
	      case (state)
		2'b00: begin scl <= 1'b0; sda <= 1'b1; end
		2'b01: begin scl <= 1'b1; sda <= 1'b1; end
		2'b10: begin scl <= 1'b1; sda <= 1'b1; data[command[3:0]] <= sda_pin; end
		2'b11: begin scl <= 1'b0; sda <= 1'b1; end
	      endcase
	  `ADDR0,`ADDR1,`ADDR2,`ADDR3,`ADDR4,`ADDR5,`ADDR6,`ADDR7,
	    `ADDR8,`ADDR9,`ADDRa,`ADDRb,`ADDRc,`ADDRd,`ADDRe,`ADDRf:
	      case (state)
		2'b00: begin scl <= 1'b0; sda <= pc[command[3:0]]; end
		2'b01: begin scl <= 1'b1; sda <= pc[command[3:0]]; end
		2'b10: begin scl <= 1'b1; sda <= pc[command[3:0]]; end
		2'b11: begin scl <= 1'b0; sda <= pc[command[3:0]]; end
	      endcase
	endcase
     end
endmodule

module video_ram_access(clk, s, row, bit_read, bit_to_write, addr, n_we, data);
   input 	    clk;
   input [6:0] 	    s;
   input [3:0]      row;
   input   	    bit_to_write;
   output  	    bit_read;
   inout 	    data;

   output 	    n_we;
   output [8:0]     addr;

   assign 	    bit_read = data;
   assign 	    n_we = ~(s[1:0] == 2'b11);
   assign 	    addr = { row, s[6:2] };
   assign 	    data = (s[1] == 1'b0) ? 1'bZ : bit_to_write;
endmodule

module gdpy(system_clk, s_clk, s_row, s_bit, s_latch, s_strobe,
	    scl_pin, sda_pin, addr, n_we, data);
   input        system_clk;
   output 	s_clk, s_row, s_bit, s_latch, s_strobe;
   inout 	scl_pin;
   inout 	sda_pin;
   output [8:0] addr;
   output 	n_we;
   inout 	data;

   wire 	clk;
   wire [3:0] 	row;
   wire [6:0] 	s;
   wire 	bit_read, bit_to_write, opcode;
   wire [15:0] 	oprand;

   divider DIV (system_clk, clk);
   video_ram_access VRAM (clk, s, row,
			  bit_read, bit_to_write, addr, n_we, data);
   matrix_driver MTX (clk, s, row, bit_read, bit_to_write, 
		      opcode, oprand, 
		      s_clk, s_row, s_bit, s_latch, s_strobe);
   i2c_srom_fetch I2CROM (clk, s, row, scl_pin, sda_pin, opcode, oprand);
endmodule
