/src/project.v
/*
 * Copyright (c) 2024 Chip Black
 * SPDX-License-Identifier: Apache-2.0
 */

`default_nettype none

module clock_generator(
  input wire clk,                  // 25.175MHz main clock
  input wire rst_n,                // active low reset
  output wire [5:0] pwm_clock,     // 393kHz PWM clock (main / 64)
  input wire vsync,                // ~60Hz video clock
  output wire [1:0] atick_clock,   // audio tick clock state, used for volume modulation
  output wire [3:0] pattern_clock  // pattern clock, increments with audio_tick
);
  reg [5:0] r_pwm_clock;
  reg [2:0] r_atick_clock;
  reg [3:0] r_pattern_clock;

  always @(posedge clk, negedge rst_n) begin
    if (!rst_n)
      r_pwm_clock <= 0;
    else
      r_pwm_clock <= r_pwm_clock + 1;
  end
  
  assign pwm_clock = r_pwm_clock;

  always @(posedge vsync, negedge rst_n) begin
    if (!rst_n)
      r_atick_clock <= 0;
    else begin
      // Divide vsync by 6 to get 10Hz sequencer clock
      // 10Hz = 600 ticks per minute
      // four ticks per beat
      // 150 BPM
      if (r_atick_clock == 5)
        r_atick_clock <= 0;
      else
        r_atick_clock <= r_atick_clock + 1;
    end
  end

  assign atick_clock = r_atick_clock[2:1];
  wire audio_tick = (r_atick_clock == 0);

  always @(posedge audio_tick, negedge rst_n) begin
    if (!rst_n)
      r_pattern_clock <= 0;
    else
      r_pattern_clock <= r_pattern_clock + 1;
  end

  assign pattern_clock = r_pattern_clock;

endmodule

module tt_um_bytex64_munch (
    input  wire [7:0] ui_in,    // Dedicated inputs
    output wire [7:0] uo_out,   // Dedicated outputs
    input  wire [7:0] uio_in,   // IOs: Input path
    output wire [7:0] uio_out,  // IOs: Output path
    output wire [7:0] uio_oe,   // IOs: Enable path (active high: 0=input, 1=output)
    input  wire       ena,      // always 1 when the design is powered, so you can ignore it
    input  wire       clk,      // clock
    input  wire       rst_n     // reset_n - low to reset
);

  // Video timing/output signals
  wire [9:0] hpos;
  wire [9:0] vpos;
  wire hsync, vsync;
  wire display_on;
  wire [1:0] R;
  wire [1:0] G;
  wire [1:0] B;

  // Video generation signals
  wire [5:0] layer [1:0];
  wire [5:0] pixel_color;
  wire [2:0] dither;
  wire [1:0] bright [7:0];  // 8 2-bit brightness levels
  wire [5:0] palette [7:0]; // 8 6-bit colors
  wire [2:0] munch_level;   // brightness output for munch module
  wire text_pixel;          // on/off output for text module

  // Audio signals
  wire audio;               // Audio output (~200kHz PWM)
  wire [5:0] pwm_clock;
  wire [1:0] atick_clock;
  wire [3:0] pattern_clock;

  // Misc signals
  wire [5:0] lfsr;
  wire [1:0] stage;
  wire [3:0] stage_timer;

  // State
  reg [6:0] counter;        // general 7-bit counter

  clock_generator clock_gen(
    .clk(clk),
    .rst_n(rst_n),
    .pwm_clock(pwm_clock),
    .vsync(vsync),
    .atick_clock(atick_clock),
    .pattern_clock(pattern_clock)
  );

  lfsr lfsr_dev(
    .clk(clk),
    .rst_n(rst_n),
    .bits(lfsr)
  );

  audio audio_mod(
    .pwm_clock(pwm_clock),
    .atick_clock(atick_clock),
    .pattern_clock(pattern_clock),
    .rst_n(rst_n),
    .rng(lfsr[0]),
    .audio(audio)
  );

  hvsync_generator hvsync_gen(
    .clk(clk),
    .reset(!rst_n),
    .hsync(hsync),
    .vsync(vsync),
    .display_on(display_on),
    .hpos(hpos),
    .vpos(vpos)
  );

  assign palette[0] = 6'b000000;
  assign palette[1] = 6'b110000;
  assign palette[2] = 6'b001100;
  assign palette[3] = 6'b000011;
  assign palette[4] = 6'b001111;
  assign palette[5] = 6'b110011;
  assign palette[6] = 6'b111100;
  assign palette[7] = 6'b111111;

  assign dither[0] = lfsr[4] & lfsr[5]; // 1/4
  assign dither[1] = lfsr[3];           // 1/2
  assign dither[2] = lfsr[1] | lfsr[2]; // 3/4
/*
  assign dither[0] = hpos[0] & vpos[0];
  assign dither[1] = hpos[0] ^ vpos[0];
  assign dither[2] = hpos[0] | vpos[0];
*/

  assign bright[0] = 2'b00;
  assign bright[1] = {1'b0, dither[0]};
  assign bright[2] = {1'b0, dither[1]};
  assign bright[3] = {1'b0, dither[2]};
  assign bright[4] = 2'b01;
  assign bright[5] = {dither[0], dither[2]};
  assign bright[6] = 2'b10;
  assign bright[7] = 2'b11;

  munch munch_gen(
    .counter(counter),
    .hpos(hpos[8:2]),
    .vpos(vpos[8:2]),
    .level(munch_level)
  );

  text_sequencer text_gen(
    .selector(pattern_clock[3:2]),
    .hpos(hpos),
    .vpos(vpos),
    .stage(stage),
    .stage_timer(stage_timer),
    .pixel(text_pixel)
  );

  stage_sequencer stage_seq_inst(
    .seq_clk(!pattern_clock[3]),
    .rst_n(rst_n),
    .stage(stage),
    .stage_timer(stage_timer)
  );

  reg [2:0] background_color;

  wire [2:0] munch_color = stage == 0 ? 0 : (stage != 3 ? 2 : ~background_color);
  wire [5:0] text_color = stage == 1 && pattern_clock[3:2] != 2'b10 ? palette[0] :
    (stage == 3 && pattern_clock[3:2] == 2'b10 ? palette[5] & {3{bright[5]}} :
      palette[7]);

  assign layer[0] = palette[munch_color] & {3{bright[munch_level]}};
  // color bars
  //assign layer[0] = palette[vpos[6:4]] & {3{bright[hpos[6:4]]}};
  assign layer[1] = text_pixel ? text_color : 0;

  assign pixel_color = layer[1] != 0 ? layer[1] :
    (layer[0] != 0 ? layer[0] : palette[background_color]);

  always @(negedge pattern_clock[1], negedge rst_n) begin
    if (!rst_n)
      background_color <= 0;
    else
      if (stage == 3)
        background_color <= lfsr[2:0];
  end

  always @(posedge vsync, negedge rst_n) begin
    if (!rst_n)
      counter <= 0;
    else
      counter <= counter + 1;
  end

  assign R = display_on ? pixel_color[5:4] : 2'b00;
  assign G = display_on ? pixel_color[3:2] : 2'b00;
  assign B = display_on ? pixel_color[1:0] : 2'b00;

  assign uo_out  = {!hsync, B[0], G[0], R[0], !vsync, B[1], G[1], R[1]};
  assign uio_out = {audio, pattern_clock[3:1], 4'b0};
  assign uio_oe  = 8'b11110000;

  // List all unused inputs to prevent warnings
  wire _unused = &{ena, ui_in, uio_in, 1'b0};
endmodule

module lfsr(
  input wire clk,
  input wire rst_n,
  output wire [5:0] bits
);
  reg [10:0] lfsr;          // LFSR

  always @(posedge clk, negedge rst_n) begin
    if (!rst_n)
      lfsr <= 11'h0;
    else
      lfsr <= {lfsr[0] ~^ lfsr[2], lfsr[10:1]};
  end

  assign bits = lfsr[5:0];
endmodule

module stage_sequencer(
  input wire seq_clk,
  input wire rst_n,
  output wire [1:0] stage,
  output wire [3:0] stage_timer
);
  reg [3:0] timer;
  reg [1:0] stage_seq;

  wire [3:0] stage_timings [3:0];

  assign stage_timings[0] = 3;
  assign stage_timings[1] = 3;
  assign stage_timings[2] = 7;
  assign stage_timings[3] = 0;

  always @(posedge seq_clk, negedge rst_n) begin
    if (!rst_n) begin
      stage_seq <= 0;
      timer <= stage_timings[0];
    end
    else begin
      if (stage_timings[stage_seq] != 0) begin
        if (timer == 0) begin
          stage_seq <= stage_seq + 1;
          timer <= stage_timings[stage_seq + 1];
        end
        else
          timer <= timer - 1;
      end
    end
  end

  assign stage = stage_seq;
  assign stage_timer = timer;

endmodule