// Demo 5: Drive a program with a cascade memory

// The code below is identical to demo 4, just condensed
module AddN#(
  parameter N = 1
)(
  input  wire[31:0] x,
  output wire[31:0] y
);
  assign y = x + N;
endmodule

module Pipeline#(
  parameter K = 8
)(
  input  wire       clock,
  input  wire[31:0] x,
  output wire[31:0] y
);
  genvar i;
  for (i = 1; i <= K; i = i + 1) begin : Adders
    wire[31:0] xi;
    reg [31:0] ri = 0;
    always @(posedge clock) 
      ri <= xi;
    
    if (i == 1) 
      AddN#(i) adder(x, xi);
    else
      AddN#(i) adder(Adders[i-1].ri, xi);
  end

  assign y = Adders[K].ri;
endmodule

// Different from demo 4: Connect inputs and outputs to memories
// See https://github.com/vmware/cascade/README.md for more on how memories work
integer COUNT = 0;
wire[31:0] v0;
wire[31:0] v10;

(* __file="in.hex" *)
Memory#(5, 8) in_mem(
  .clock(clock.val),
  .raddr1(COUNT),
  .rdata1(v0)
);

localparam DEPTH = 10;
Pipeline#(DEPTH) pipeline(clock.val, v0, v10);

(* __file="out.hex" *)
Memory#(5, 8) out_mem(
  .clock(clock.val),
  .wen(COUNT >= 10),
  .waddr(COUNT-10),
  .wdata(v10)
);

always @(posedge clock.val) begin
  COUNT <= COUNT + 1;
  if (COUNT == 10+32) 
    $finish;
end

// Things to do:
// 1. Try changing the values in in.mem. Run the program and observe outputs in out.mem.
