diff --git a/timing/fuzzers/MachXO2/010-basic-cells/fuzzer.py b/timing/fuzzers/MachXO2/010-basic-cells/fuzzer.py index 3e829cd5..32025952 100644 --- a/timing/fuzzers/MachXO2/010-basic-cells/fuzzer.py +++ b/timing/fuzzers/MachXO2/010-basic-cells/fuzzer.py @@ -9,7 +9,7 @@ def rewrite_celltype_XO2(name, type): return type.split('/')[-1].split("_")[0] def main(): - cell_fuzzers.build_and_add(["../../../resource/picorv32_large.v", "../../../resource/picorv32_large_blockram.v", "../../../resource/distributed_ram.v"], density="7000", family="MachXO2", inc_cell=include_cell_XO2, rw_cell_func=rewrite_celltype_XO2) + cell_fuzzers.build_and_add(["../../../resource/picorv32_large.v", "../../../resource/picorv32_large_blockram.v", "../../../resource/distributed_ram.v", "../../../resource/altair.v", "../../../resource/jt49.v", "../../../resource/jt5205.v", "../../../resource/jt7759.v"], density="7000", family="MachXO2", inc_cell=include_cell_XO2, rw_cell_func=rewrite_celltype_XO2) if __name__ == "__main__": diff --git a/timing/resource/altair.v b/timing/resource/altair.v new file mode 100644 index 00000000..e8f153f6 --- /dev/null +++ b/timing/resource/altair.v @@ -0,0 +1,853 @@ +module top +( + input clk, + input rx, + output tx, + input rx2, + output tx2 +); + + reg [5:0] reset_cnt = 0; + wire resetn = &reset_cnt; + + always @(posedge clk) begin + reset_cnt <= reset_cnt + !resetn; + end + + altair machine0(.clk(clk),.reset(~resetn),.rx(rx),.tx(tx)); + altair machine1(.clk(clk),.reset(~resetn),.rx(rx2),.tx(tx2)); + +endmodule + +module altair( + input clk, + input reset, + input rx, + output tx +); + reg ce = 0; + reg intr = 0; + reg [7:0] idata; + wire [15:0] addr; + wire rd; + wire wr_n; + wire inta_n; + wire [7:0] odata; + wire inte_o; + wire sync; + + // Memory is sync so need one more clock to write/read + // This slows down CPU + always @(posedge clk) begin + ce <= !ce; + end + + reg[7:0] sysctl; + + wire [7:0] rom_out; + wire [7:0] ram_out; + wire [7:0] rammain_out; + wire [7:0] boot_out; + wire [7:0] sio_out; + + + wire boot; + + reg wr_ram; + reg wr_rammain; + reg wr_sio; + + reg rd_boot; + reg rd_ram; + reg rd_rammain; + reg rd_rom; + reg rd_sio; + + always @(*) + begin + rd_boot = 0; + rd_ram = 0; + rd_rammain = 0; + rd_rom = 0; + rd_sio = 0; + idata = 8'hff; + casex ({boot,sysctl[6],addr[15:8]}) + // Turn-key BOOT + {2'b10,8'bxxxxxxxx}: begin idata = boot_out; rd_boot = rd; end // any address + // MEM MAP + {2'b00,8'b000xxxxx}: begin idata = rammain_out; rd_rammain = rd; end // 0x0000-0x1fff + {2'b00,8'b11111011}: begin idata = ram_out; rd_ram = rd; end // 0xfb00-0xfbff + {2'b00,8'b11111101}: begin idata = rom_out; rd_rom = rd; end // 0xfd00-0xfdff + // I/O MAP - addr[15:8] == addr[7:0] for this section + {2'b01,8'b000x000x}: begin idata = sio_out; rd_sio = rd; end // 0x00-0x01 0x10-0x11 + endcase + end + + always @(*) + begin + wr_ram = 0; + wr_sio = 0; + wr_rammain = 0; + + casex ({sysctl[4],addr[15:8]}) + // MEM MAP + {1'b0,8'b000xxxxx}: wr_rammain = ~wr_n; // 0x0000-0x1fff + {1'b0,8'b11111011}: wr_ram = ~wr_n; // 0xfb00-0xfbff + // 0xfd00-0xfdff read-only + // I/O MAP - addr[15:8] == addr[7:0] for this section + {1'b1,8'b000x000x}: wr_sio = ~wr_n; // 0x00-0x01 0x10-0x11 + endcase + end + + always @(posedge clk) + begin + if (sync) sysctl <= odata; + end + + i8080 cpu(.clk(clk),.ce(ce),.reset(reset),.intr(intr),.idata(idata),.addr(addr),.sync(sync),.rd(rd),.wr_n(wr_n),.inta_n(inta_n),.odata(odata),.inte_o(inte_o)); + + jmp_boot boot_ff(.clk(clk),.reset(reset),.rd(rd_boot),.data_out(boot_out),.valid(boot)); + + rom_memory #(.ADDR_WIDTH(8),.FILENAME("turnmon.bin.mem")) rom(.clk(clk),.addr(addr[7:0]),.rd(rd_rom),.data_out(rom_out)); + + ram_memory #(.ADDR_WIDTH(8)) stack(.clk(clk),.addr(addr[7:0]),.data_in(odata),.rd(rd_ram),.we(wr_ram),.data_out(ram_out)); + + ram_memory #(.ADDR_WIDTH(13),.FILENAME("basic4k32.bin.mem")) mainmem(.clk(clk),.addr(addr[12:0]),.data_in(odata),.rd(rd_rammain),.we(wr_rammain),.data_out(rammain_out)); + + mc6850 sio(.clk(clk),.reset(reset),.addr(addr[0]),.data_in(odata),.rd(rd_sio),.we(wr_sio),.data_out(sio_out),.ce(ce),.rx(rx),.tx(tx)); + +endmodule + +// ==================================================================== +// Bashkiria-2M FPGA REPLICA +// +// Copyright (C) 2010 Dmitry Tselikov +// +// This core is distributed under modified BSD license. +// For complete licensing information see LICENSE.TXT. +// -------------------------------------------------------------------- +// +// An open implementation of Bashkiria-2M home computer +// +// Author: Dmitry Tselikov http://bashkiria-2m.narod.ru/ +// +// Design File: k580wm80a.v +// +// Processor k580wm80a core design file of Bashkiria-2M replica. + +module i8080( + input clk, + input ce, + input reset, + input intr, + input [7:0] idata, + output reg [15:0] addr, + output reg sync, + output rd, + output reg wr_n, + output inta_n, + output reg [7:0] odata, + output inte_o); + +reg M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16,M17,T5; +reg[2:0] state; + +wire M1n = M2|M3|M4|M5|M6|M7|M8|M9|M10|M11|M12|M13|M14|M15|M16|M17; + +reg[15:0] PC; +reg[15:0] SP; +reg[7:0] B,C,D,E,H,L,A; +reg[7:0] W,Z,IR; +reg[9:0] ALU; +reg FS,FZ,FA,FP,FC,_FA; + +reg rd_,intproc; +assign rd = rd_&~intproc; +assign inta_n = ~(rd_&intproc); +assign inte_o = inte[1]; + +reg[1:0] inte; +reg jmp,call,halt; +reg save_alu,save_a,save_r,save_rp,read_r,read_rp; +reg incdec,xthl,xchg,sphl,daa; +reg ccc; + +always @(*) begin + casex (IR[5:3]) + 3'b00x: ALU = {1'b0,A,1'b1}+{1'b0,Z,FC&IR[3]}; + 3'b01x: ALU = {1'b0,A,1'b0}-{1'b0,Z,FC&IR[3]}; + 3'b100: ALU = {1'b0,A & Z,1'b0}; + 3'b101: ALU = {1'b0,A ^ Z,1'b0}; + 3'b110: ALU = {1'b0,A | Z,1'b0}; + 3'b111: ALU = {1'b0,A,1'b0}-{1'b0,Z,1'b0}; + endcase +end + +always @(*) begin + casex (IR[5:3]) + 3'b00x: _FA = A[4]^Z[4]^ALU[5]; + 3'b100: _FA = A[3]|Z[3]; + 3'b101: _FA = 1'b0; + 3'b110: _FA = 1'b0; + default: _FA = ~(A[4]^Z[4]^ALU[5]); + endcase +end + +always @(*) begin + // SZ.A.P.C + case(idata[5:3]) + 3'h0: ccc = ~FZ; + 3'h1: ccc = FZ; + 3'h2: ccc = ~FC; + 3'h3: ccc = FC; + 3'h4: ccc = ~FP; + 3'h5: ccc = FP; + 3'h6: ccc = ~FS; + 3'h7: ccc = FS; + endcase +end + +wire[7:0] F = {FS,FZ,1'b0,FA,1'b0,FP,1'b1,FC}; +wire[7:0] Z1 = incdec ? Z+{{7{IR[0]}},1'b1} : Z; +wire[15:0] WZ1 = incdec ? {W,Z}+{{15{IR[3]}},1'b1} : {W,Z}; +wire[3:0] daaZL = FA!=0 || A[3:0] > 4'h9 ? 4'h6 : 4'h0; +wire[3:0] daaZH = FC!=0 || A[7:4] > {3'b100, A[3:0]>4'h9 ? 1'b0 : 1'b1} ? 4'h6 : 4'h0; + +always @(posedge clk or posedge reset) +begin + if (reset) begin + {M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16,M17} <= 0; + state <= 0; PC <= 0; {FS,FZ,FA,FP,FC} <= 0; {addr,odata} <= 0; + {sync,rd_,jmp,halt,inte,save_alu,save_a,save_r,save_rp,incdec,intproc} <= 0; + wr_n <= 1'b1; + end else if (ce) begin + sync <= 0; rd_ <= 0; wr_n <= 1'b1; + if (halt&~(M1|(intr&inte[1]))) begin + sync <= 1'b1; // state: rd in m1 out hlt stk ~wr int + odata <= 8'b10001010; // rd? hlt ~wr + end else + if (M1|~M1n) begin + case (state) + 3'b000: begin + halt <= 0; intproc <= intr&inte[1]; inte[1] <= inte[0]; + M1 <= 1'b1; + sync <= 1'b1; + odata <= {7'b1010001,intr&inte[1]}; // rd m1 ~wr + addr <= jmp ? {W,Z} : PC; + state <= 3'b001; + if (intr&inte[1]) inte <= 2'b0; + if (save_alu) begin + FS <= ALU[8]; + FZ <= ~|ALU[8:1]; + FA <= _FA; + FP <= ~^ALU[8:1]; + FC <= ALU[9]|(FC&daa); + if (IR[5:3]!=3'b111) A <= ALU[8:1]; + end else + if (save_a) begin + A <= Z1; + end else + if (save_r) begin + case (IR[5:3]) + 3'b000: B <= Z1; + 3'b001: C <= Z1; + 3'b010: D <= Z1; + 3'b011: E <= Z1; + 3'b100: H <= Z1; + 3'b101: L <= Z1; + 3'b111: A <= Z1; + endcase + if (incdec) begin + FS <= Z1[7]; + FZ <= ~|Z1; + FA <= IR[0] ? Z1[3:0]!=4'b1111 : Z1[3:0]==0; + FP <= ~^Z1; + end + end else + if (save_rp) begin + case (IR[5:4]) + 2'b00: {B,C} <= WZ1; + 2'b01: {D,E} <= WZ1; + 2'b10: {H,L} <= WZ1; + 2'b11: + if (sphl || !IR[7]) begin + SP <= WZ1; + end else begin + {A,FS,FZ,FA,FP,FC} <= {WZ1[15:8],WZ1[7],WZ1[6],WZ1[4],WZ1[2],WZ1[0]}; + end + endcase + end + end + 3'b001: begin + rd_ <= 1'b1; + PC <= addr+{15'b0,~intproc}; + state <= 3'b010; + end + 3'b010: begin + IR <= idata; + {jmp,call,save_alu,save_a,save_r,save_rp,read_r,read_rp,incdec,xthl,xchg,sphl,T5,daa} <= 0; + casex (idata) + 8'b00xx0001: {save_rp,M2,M3} <= 3'b111; + 8'b00xx1001: {read_rp,M16,M17} <= 3'b111; + 8'b000x0010: {read_rp,M14} <= 2'b11; + 8'b00100010: {M2,M3,M14,M15} <= 4'b1111; + 8'b00110010: {M2,M3,M14} <= 3'b111; + 8'b000x1010: {read_rp,save_a,M12} <= 3'b111; + 8'b00101010: {save_rp,M2,M3,M12,M13} <= 5'b11111; + 8'b00111010: {save_a,M2,M3,M12} <= 4'b1111; + 8'b00xxx011: {read_rp,save_rp,incdec,T5} <= 4'b1111; + 8'b00xxx10x: {read_r,save_r,incdec,T5} <= {3'b111,idata[5:3]!=3'b110}; + 8'b00xxx110: {save_r,M2} <= 2'b11; + 8'b00000111: {FC,A} <= {A,A[7]}; + 8'b00001111: {A,FC} <= {A[0],A}; + 8'b00010111: {FC,A} <= {A,FC}; + 8'b00011111: {A,FC} <= {FC,A}; + 8'b00100111: {daa,save_alu,IR[5:3],Z} <= {5'b11000,daaZH,daaZL}; + 8'b00101111: A <= ~A; + 8'b00110111: FC <= 1'b1; + 8'b00111111: FC <= ~FC; + 8'b01xxxxxx: if (idata[5:0]==6'b110110) halt <= 1'b1; else {read_r,save_r,T5} <= {2'b11,~(idata[5:3]==3'b110||idata[2:0]==3'b110)}; + 8'b10xxxxxx: {read_r,save_alu} <= 2'b11; + 8'b11xxx000: {jmp,M8,M9} <= {3{ccc}}; + 8'b11xx0001: {save_rp,M8,M9} <= 3'b111; + 8'b110x1001: {jmp,M8,M9} <= 3'b111; + 8'b11101001: {read_rp,jmp,T5} <= 3'b111; + 8'b11111001: {read_rp,save_rp,T5,sphl} <= 4'b1111; + 8'b11xxx010: {jmp,M2,M3} <= {ccc,2'b11}; + 8'b1100x011: {jmp,M2,M3} <= 3'b111; + 8'b11010011: {M2,M7} <= 2'b11; + 8'b11011011: {M2,M6} <= 2'b11; + 8'b11100011: {save_rp,M8,M9,M10,M11,xthl} <= 6'b111111; + 8'b11101011: {read_rp,save_rp,xchg} <= 3'b111; + 8'b1111x011: inte <= idata[3] ? 2'b1 : 2'b0; + 8'b11xxx100: {jmp,M2,M3,T5,M10,M11,call} <= {ccc,3'b111,{3{ccc}}}; + 8'b11xx0101: {read_rp,T5,M10,M11} <= 4'b1111; + 8'b11xx1101: {jmp,M2,M3,T5,M10,M11,call} <= 7'b1111111; + 8'b11xxx110: {save_alu,M2} <= 2'b11; + 8'b11xxx111: {jmp,T5,M10,M11,call,W,Z} <= {5'b11111,10'b0,idata[5:3],3'b0}; + endcase + state <= 3'b011; + end + 3'b011: begin + if (read_rp) begin + case (IR[5:4]) + 2'b00: {W,Z} <= {B,C}; + 2'b01: {W,Z} <= {D,E}; + 2'b10: {W,Z} <= xchg ? {D,E} : {H,L}; + 2'b11: {W,Z} <= sphl ? {H,L} : IR[7] ? {A,F} : SP; + endcase + if (xchg) {D,E} <= {H,L}; + end else + if (~(jmp|daa)) begin + case (incdec?IR[5:3]:IR[2:0]) + 3'b000: Z <= B; + 3'b001: Z <= C; + 3'b010: Z <= D; + 3'b011: Z <= E; + 3'b100: Z <= H; + 3'b101: Z <= L; + 3'b110: M4 <= read_r; + 3'b111: Z <= A; + endcase + M5 <= save_r && IR[5:3]==3'b110; + end + state <= T5 ? 3'b100 : 0; + M1 <= T5; + end + 3'b100: begin + if (M10) SP <= SP-16'b1; + state <= 0; + M1 <= 0; + end + endcase + end else + if (M2 || M3) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b1000001,intproc}; // rd ~wr + addr <= PC; + state <= 3'b001; + end + 3'b001: begin + rd_ <= 1'b1; + PC <= addr+{15'b0,~intproc}; + state <= 3'b010; + end + 3'b010: begin + if (M2) begin + Z <= idata; + M2 <= 0; + end else begin + W <= idata; + M3 <= 0; + end + state <= 3'b000; + end + endcase + end else + if (M4) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b1000001,intproc}; // rd ~wr + addr <= {H,L}; + state <= 3'b001; + end + 3'b001: begin + rd_ <= 1'b1; + state <= 3'b010; + end + 3'b010: begin + Z <= idata; + M4 <= 0; + state <= 3'b000; + end + endcase + end else + if (M5) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b0000000,intproc}; // ~wr=0 + addr <= {H,L}; + state <= 3'b001; + end + 3'b001: begin + odata <= Z1; + wr_n <= 1'b0; + state <= 3'b010; + end + 3'b010: begin + M5 <= 0; + state <= 3'b000; + end + endcase + end else + if (M6) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b0100001,intproc}; // in ~wr + addr <= {Z,Z}; + state <= 3'b001; + end + 3'b001: begin + rd_ <= 1'b1; + state <= 3'b010; + end + 3'b010: begin + A <= idata; + M6 <= 0; + state <= 3'b000; + end + endcase + end else + if (M7) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b0001000,intproc}; // out + addr <= {Z,Z}; + state <= 3'b001; + end + 3'b001: begin + odata <= A; + wr_n <= 1'b0; + state <= 3'b010; + end + 3'b010: begin + M7 <= 0; + state <= 3'b000; + end + endcase + end else + if (M8 || M9) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b1000011,intproc}; // rd stk ~wr + addr <= SP; + state <= 3'b001; + end + 3'b001: begin + rd_ <= 1'b1; + if (M8 || !xthl) SP <= SP+16'b1; + state <= 3'b010; + end + 3'b010: begin + if (M8) begin + Z <= idata; + M8 <= 0; + end else begin + W <= idata; + M9 <= 0; + end + state <= 3'b000; + end + endcase + end else + if (M10 || M11) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b0000010,intproc}; // stk + addr <= SP; + state <= 3'b001; + end + 3'b001: begin + if (M10) begin + SP <= SP-16'b1; + odata <= xthl ? H : call ? PC[15:8] : W; + end else begin + odata <= xthl ? L : call ? PC[7:0] : Z; + end + wr_n <= 1'b0; + state <= 3'b010; + end + 3'b010: begin + if (M10) begin + M10 <= 0; + end else begin + M11 <= 0; + end + state <= 3'b000; + end + endcase + end else + if (M12 || M13) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b1000001,intproc}; // rd ~wr + addr <= M12 ? {W,Z} : addr+16'b1; + state <= 3'b001; + end + 3'b001: begin + rd_ <= 1'b1; + state <= 3'b010; + end + 3'b010: begin + if (M12) begin + Z <= idata; + M12 <= 0; + end else begin + W <= idata; + M13 <= 0; + end + state <= 3'b000; + end + endcase + end else + if (M14 || M15) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b0000000,intproc}; // ~wr=0 + addr <= M14 ? {W,Z} : addr+16'b1; + state <= 3'b001; + end + 3'b001: begin + if (M14) begin + odata <= M15 ? L : A; + end else begin + odata <= H; + end + wr_n <= 1'b0; + state <= 3'b010; + end + 3'b010: begin + if (M14) begin + M14 <= 0; + end else begin + M15 <= 0; + end + state <= 3'b000; + end + endcase + end else + if (M16 || M17) begin + case (state) + 3'b000: begin + sync <= 1'b1; + odata <= {7'b0000001,intproc}; // ~wr + state <= 3'b001; + end + 3'b001: begin + state <= 3'b010; + end + 3'b010: begin + if (M16) begin + M16 <= 0; + end else begin + {FC,H,L} <= {1'b0,H,L}+{1'b0,W,Z}; + M17 <= 0; + end + state <= 3'b000; + end + endcase + end + end +end + +endmodule + +module jmp_boot( + input clk, + input reset, + input rd, + output reg [7:0] data_out, + output reg valid +); + reg [1:0] state = 0; + reg prev_rd = 0; + always @(posedge clk) + begin + if (reset) + begin + state <= 0; + valid <= 1; + end + else + begin + if (rd && prev_rd==0) + begin + case (state) + 2'b00 : begin + data_out <= 8'b11000011; // JMP 0xfd00 + state <= 2'b01; + end + 2'b01 : begin + data_out <= 8'h00; + state <= 2'b10; + end + 2'b10 : begin + data_out <= 8'hFD; + state <= 2'b11; + end + 2'b11 : begin + state <= 2'b11; + valid <= 0; + end + endcase + end + prev_rd = rd; + end + end +endmodule + +module mc6850( + input clk, + input reset, + input addr, + input [7:0] data_in, + input rd, + input we, + output reg [7:0] data_out, + input ce, + input rx, + output tx +); + wire valid; + wire tdre; + wire [7:0] uart_out; + wire dat_wait; + + simpleuart uart( + .clk(clk), + .resetn(~reset), + + .ser_tx(tx), + .ser_rx(rx), + + .cfg_divider(12000000/9600), + + .reg_dat_we(we && (addr==1'b1)), + .reg_dat_re(rd && (addr==1'b1)), + .reg_dat_di(data_in & 8'h7f), + .reg_dat_do(uart_out), + .reg_dat_wait(dat_wait), + .recv_buf_valid(valid), + .tdre(tdre) +); + + always @(posedge clk) + begin + if (rd) + begin + if (addr==1'b0) + data_out <= { 2'b00,valid, 1'b0, 2'b00, tdre, valid }; + else + data_out <= uart_out; + end + end +endmodule + +module ram_memory( + input clk, + input [7:0] addr, + input [7:0] data_in, + input rd, + input we, + output reg [7:0] data_out +); + parameter integer ADDR_WIDTH = 8; + + reg [7:0] ram[0:(2 ** ADDR_WIDTH)-1] /* verilator public_flat */; + + parameter FILENAME = ""; + + always @(posedge clk) + begin + if (we) + ram[addr] <= data_in; + if (rd) + data_out <= ram[addr]; + end +endmodule + +module rom_memory( + input clk, + input [7:0] addr, + input rd, + output reg [7:0] data_out +); + parameter FILENAME = ""; + + parameter integer ADDR_WIDTH = 8; + + reg [7:0] rom[0:(2 ** ADDR_WIDTH)-1] /* verilator public_flat */; + + always @(posedge clk) + begin + if (rd) + data_out <= rom[addr]; + end +endmodule + + /* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module simpleuart ( + input clk, + input resetn, + + output ser_tx, + input ser_rx, + + input [31:0] cfg_divider, + + input reg_dat_we, + input reg_dat_re, + input [7:0] reg_dat_di, + output [7:0] reg_dat_do, + output reg_dat_wait, + output reg recv_buf_valid, + output reg tdre +); + + reg [3:0] recv_state; + reg [31:0] recv_divcnt; + reg [7:0] recv_pattern; + reg [7:0] recv_buf_data; + + reg [9:0] send_pattern; + reg [3:0] send_bitcnt; + reg [31:0] send_divcnt; + reg send_dummy; + + assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy); + assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0; + + always @(posedge clk) begin + if (!resetn) begin + recv_state <= 0; + recv_divcnt <= 0; + recv_pattern <= 0; + recv_buf_data <= 0; + recv_buf_valid <= 0; + end else begin + recv_divcnt <= recv_divcnt + 1; + if (reg_dat_re) + recv_buf_valid <= 0; + case (recv_state) + 0: begin + if (!ser_rx) + recv_state <= 1; + recv_divcnt <= 0; + end + 1: begin + if (2*recv_divcnt > cfg_divider) begin + recv_state <= 2; + recv_divcnt <= 0; + end + end + 10: begin + if (recv_divcnt > cfg_divider) begin + recv_buf_data <= recv_pattern; + recv_buf_valid <= 1; + recv_state <= 0; + end + end + default: begin + if (recv_divcnt > cfg_divider) begin + recv_pattern <= {ser_rx, recv_pattern[7:1]}; + recv_state <= recv_state + 1; + recv_divcnt <= 0; + end + end + endcase + end + end + + assign ser_tx = send_pattern[0]; + + always @(posedge clk) begin + send_divcnt <= send_divcnt + 1; + if (!resetn) begin + send_pattern <= ~0; + send_bitcnt <= 0; + send_divcnt <= 0; + send_dummy <= 1; + tdre <=0; + end else begin + if (send_dummy && !send_bitcnt) begin + send_pattern <= ~0; + send_bitcnt <= 15; + send_divcnt <= 0; + send_dummy <= 0; + end else + if (reg_dat_we && !send_bitcnt) begin + send_pattern <= {1'b1, reg_dat_di, 1'b0}; + send_bitcnt <= 10; + send_divcnt <= 0; + tdre <=0; + end else + if (send_divcnt > cfg_divider && send_bitcnt) begin + send_pattern <= {1'b1, send_pattern[9:1]}; + send_bitcnt <= send_bitcnt - 1; + send_divcnt <= 0; + end else if (send_bitcnt==0) + begin + tdre <=1; + end + end + end +endmodule + diff --git a/timing/resource/jt49.v b/timing/resource/jt49.v new file mode 100644 index 00000000..221d122f --- /dev/null +++ b/timing/resource/jt49.v @@ -0,0 +1,695 @@ +/* This file is part of JT49. + + JT49 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, either version 3 of the License, or + (at your option) any later version. + + JT49 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT49. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 10-Nov-2018 + + Based on sqmusic, by the same author + + */ + +module jt49 ( // note that input ports are not multiplexed + input rst_n, + input clk, // signal on positive edge + input clk_en /* synthesis direct_enable = 1 */, + input [3:0] addr, + input cs_n, + input wr_n, // write + input [7:0] din, + input sel, // if sel is low, the clock is divided by 2 + output reg [7:0] dout, + output reg [9:0] sound, // combined channel output + //output reg [7:0] A, // linearised channel output + //output reg [7:0] B, + //output reg [7:0] C, + output sample + + //input [7:0] IOA_in, + //output [7:0] IOA_out, + //output IOA_oe, + + //input [7:0] IOB_in, + //output [7:0] IOB_out, + //output IOB_oe +); + +reg [7:0] A; // linearised channel output +reg [7:0] B; +reg [7:0] C; +parameter [1:0] COMP=2'b00; +parameter CLKDIV=3; +wire [1:0] comp = COMP; + +reg [7:0] regarray[15:0]; +wire [7:0] port_A, port_B; + +wire [4:0] envelope; +wire bitA, bitB, bitC; +wire noise; +reg Amix, Bmix, Cmix; + +wire cen16, cen256; + +//assign IOA_out = regarray[14]; +//assign IOB_out = regarray[15]; +//assign port_A = IOA_in; +//assign port_B = IOB_in; +//assign IOA_oe = regarray[7][6]; +//assign IOB_oe = regarray[7][7]; +assign sample = cen16; + +jt49_cen #(.CLKDIV(CLKDIV)) u_cen( + .clk ( clk ), + .rst_n ( rst_n ), + .cen ( clk_en ), + .sel ( sel ), + .cen16 ( cen16 ), + .cen256 ( cen256 ) +); + +// internal modules operate at clk/16 +jt49_div #(12) u_chA( + .clk ( clk ), + .rst_n ( rst_n ), + .cen ( cen16 ), + .period ( {regarray[1][3:0], regarray[0][7:0] } ), + .div ( bitA ) +); + +jt49_div #(12) u_chB( + .clk ( clk ), + .rst_n ( rst_n ), + .cen ( cen16 ), + .period ( {regarray[3][3:0], regarray[2][7:0] } ), + .div ( bitB ) +); + +jt49_div #(12) u_chC( + .clk ( clk ), + .rst_n ( rst_n ), + .cen ( cen16 ), + .period ( {regarray[5][3:0], regarray[4][7:0] } ), + .div ( bitC ) +); + +jt49_noise u_ng( + .clk ( clk ), + .cen ( cen16 ), + .rst_n ( rst_n ), + .period ( regarray[6][4:0] ), + .noise ( noise ) +); + +// envelope generator +wire eg_step; +wire [15:0] eg_period = {regarray[4'hc],regarray[4'hb]}; +wire null_period = eg_period == 16'h0; + +jt49_div #(16) u_envdiv( + .clk ( clk ), + .cen ( cen256 ), + .rst_n ( rst_n ), + .period ( eg_period ), + .div ( eg_step ) +); + +reg eg_restart; + +jt49_eg u_env( + .clk ( clk ), + .cen ( cen256 ), + .step ( eg_step ), + .rst_n ( rst_n ), + .restart ( eg_restart ), + .null_period( null_period ), + .ctrl ( regarray[4'hD][3:0] ), + .env ( envelope ) +); + +reg [4:0] logA, logB, logC, log; +wire [7:0] lin; + +jt49_exp u_exp( + .clk ( clk ), + .comp ( comp ), + .din ( log ), + .dout ( lin ) +); + +wire [4:0] volA = { regarray[ 8][3:0], regarray[ 8][3] }; +wire [4:0] volB = { regarray[ 9][3:0], regarray[ 9][3] }; +wire [4:0] volC = { regarray[10][3:0], regarray[10][3] }; +wire use_envA = regarray[ 8][4]; +wire use_envB = regarray[ 9][4]; +wire use_envC = regarray[10][4]; +wire use_noA = regarray[ 7][3]; +wire use_noB = regarray[ 7][4]; +wire use_noC = regarray[ 7][5]; + +reg [3:0] acc_st; + +always @(posedge clk) if( clk_en ) begin + Amix <= (noise|use_noA) & (bitA|regarray[7][0]); + Bmix <= (noise|use_noB) & (bitB|regarray[7][1]); + Cmix <= (noise|use_noC) & (bitC|regarray[7][2]); + + logA <= !Amix ? 5'd0 : (use_envA ? envelope : volA ); + logB <= !Bmix ? 5'd0 : (use_envB ? envelope : volB ); + logC <= !Cmix ? 5'd0 : (use_envC ? envelope : volC ); +end + +reg [9:0] acc; + +always @(posedge clk, negedge rst_n) begin + if( !rst_n ) begin + acc_st <= 4'b1; + acc <= 10'd0; + A <= 8'd0; + B <= 8'd0; + C <= 8'd0; + sound <= 10'd0; + end else if(clk_en) begin + acc_st <= { acc_st[2:0], acc_st[3] }; + acc <= acc + {2'b0,lin}; + case( acc_st ) + 4'b0001: begin + log <= logA; + acc <= 10'd0; + sound <= acc; + end + 4'b0010: begin + A <= lin; + log <= logB; + end + 4'b0100: begin + B <= lin; + log <= logC; + end + 4'b1000: begin // last sum + C <= lin; + end + default:; + endcase + end +end + +reg [7:0] read_mask; + +always @(*) + case(addr) + 4'h0,4'h2,4'h4,4'h7,4'hb,4'hc,4'he,4'hf: + read_mask = 8'hff; + 4'h1,4'h3,4'h5,4'hd: + read_mask = 8'h0f; + 4'h6,4'h8,4'h9,4'ha: + read_mask = 8'h1f; + endcase // addr + +// register array +wire write; +reg last_write; +wire wr_edge = write & ~last_write; + +assign write = !wr_n && !cs_n; + +always @(posedge clk, negedge rst_n) begin + if( !rst_n ) begin + dout <= 8'd0; + last_write <= 0; + eg_restart <= 0; + regarray[0]<=8'd0; regarray[4]<=8'd0; regarray[ 8]<=8'd0; regarray[12]<=8'd0; + regarray[1]<=8'd0; regarray[5]<=8'd0; regarray[ 9]<=8'd0; regarray[13]<=8'd0; + regarray[2]<=8'd0; regarray[6]<=8'd0; regarray[10]<=8'd0; regarray[14]<=8'd0; + regarray[3]<=8'd0; regarray[7]<=8'd0; regarray[11]<=8'd0; regarray[15]<=8'd0; + end else begin + last_write <= write; + // Data read + case( addr ) + 4'he: dout <= port_A; + 4'hf: dout <= port_B; + default: dout <= regarray[ addr ] & read_mask; + endcase + // Data write + if( write ) begin + regarray[addr] <= din; + if ( addr == 4'hD && wr_edge ) eg_restart <= 1; + end else begin + eg_restart <= 0; + end + end +end + +endmodule + + +/* This file is part of JT49. + + JT49 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, either version 3 of the License, or + (at your option) any later version. + + JT49 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT49. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 10-Nov-2018 + + Based on sqmusic, by the same author + + */ + +module jt49_cen( + input clk, + input rst_n, + input cen, // base clock enable signal + input sel, // when low, divide by 2 once more + output reg cen16, + output reg cen256 +); + +reg [9:0] cencnt; +parameter CLKDIV = 3; // use 3 for standalone JT49 or 2 +localparam eg = CLKDIV; //8; + +wire toggle16 = sel ? ~|cencnt[CLKDIV-1:0] : ~|cencnt[CLKDIV:0]; +wire toggle256= sel ? ~|cencnt[eg-2:0] : ~|cencnt[eg-1:0]; + + +always @(posedge clk, negedge rst_n) begin + if(!rst_n) + cencnt <= 10'd0; + else begin + if(cen) cencnt <= cencnt+10'd1; + end +end + +always @(posedge clk) begin + cen16 <= cen & toggle16; + cen256 <= cen & toggle256; +end + + +endmodule // jt49_cen + +/* This file is part of JT49. + + JT49 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, either version 3 of the License, or + (at your option) any later version. + + JT49 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT49. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 10-Nov-2018 + + Based on sqmusic, by the same author + + */ + + +module jt49_div #(parameter W=12 )( + (* direct_enable *) input cen, + input clk, // this is the divided down clock from the core + input rst_n, + input [W-1:0] period, + output reg div +); + +reg [W-1:0]count; + +wire [W-1:0] one = { {W-1{1'b0}}, 1'b1}; + +always @(posedge clk, negedge rst_n ) begin + if( !rst_n) begin + count <= one; + div <= 1'b0; + end + else if(cen) begin + if( count>=period ) begin + count <= one; + div <= ~div; + end + else + /*if( period!={W{1'b0}} )*/ count <= count + one ; + end +end + +endmodule + + +/* This file is part of JT49. + + JT49 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, either version 3 of the License, or + (at your option) any later version. + + JT49 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT49. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 10-Nov-2018 + + Based on sqmusic, by the same author + + */ + +module jt49_eg( + (* direct_enable *) input cen, + input clk, // this is the divided down clock from the core + input step, + input null_period, + input rst_n, + input restart, + input [3:0] ctrl, + output reg [4:0]env +); + +reg inv, stop; +reg [4:0] gain; + +wire CONT = ctrl[3]; +wire ATT = ctrl[2]; +wire ALT = ctrl[1]; +wire HOLD = ctrl[0]; + +wire will_hold = !CONT || HOLD; + +always @(posedge clk) + if( cen ) env <= inv ? ~gain : gain; + +reg last_step; +wire step_edge = (step && !last_step) || null_period; +wire will_invert = (!CONT&&ATT) || (CONT&&ALT); +reg rst_latch, rst_clr; + +always @(posedge clk) begin + if( restart ) rst_latch <= 1; + else if(rst_clr ) rst_latch <= 0; +end + +always @( posedge clk, negedge rst_n ) + if( !rst_n) begin + gain <= 5'h1F; + inv <= 0; + stop <= 0; + rst_clr <= 0; + end + else if( cen ) begin + last_step <= step; + if( rst_latch ) begin + gain <= 5'h1F; + inv <= ATT; + stop <= 1'b0; + rst_clr <= 1; + end + else begin + rst_clr <= 0; + if (step_edge && !stop) begin + if( gain==5'h00 ) begin + if( will_hold ) + stop <= 1'b1; + else + gain <= gain-5'b1; + if( will_invert ) inv<=~inv; + end + else gain <= gain-5'b1; + end + end + end + +endmodule + + +/* This file is part of JT49. + + JT49 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, either version 3 of the License, or + (at your option) any later version. + + JT49 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT49. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 10-Nov-2018 + + Based on sqmusic, by the same author + + */ + + +// Compression vs dynamic range +// 0 -> 43.6dB +// 1 -> 29.1 +// 2 -> 21.8 +// 3 -> 13.4 + +module jt49_exp( + input clk, + input [1:0] comp, // compression + input [4:0] din, + output reg [7:0] dout +); + +reg [7:0] lut[0:127]; + +always @(posedge clk) + dout <= lut[ {comp,din} ]; + +initial begin + lut[0] = 8'd0; + lut[1] = 8'd1; + lut[2] = 8'd1; + lut[3] = 8'd1; + lut[4] = 8'd2; + lut[5] = 8'd2; + lut[6] = 8'd3; + lut[7] = 8'd3; + lut[8] = 8'd4; + lut[9] = 8'd5; + lut[10] = 8'd6; + lut[11] = 8'd7; + lut[12] = 8'd9; + lut[13] = 8'd11; + lut[14] = 8'd13; + lut[15] = 8'd15; + lut[16] = 8'd18; + lut[17] = 8'd22; + lut[18] = 8'd26; + lut[19] = 8'd31; + lut[20] = 8'd37; + lut[21] = 8'd45; + lut[22] = 8'd53; + lut[23] = 8'd63; + lut[24] = 8'd75; + lut[25] = 8'd90; + lut[26] = 8'd107; + lut[27] = 8'd127; + lut[28] = 8'd151; + lut[29] = 8'd180; + lut[30] = 8'd214; + lut[31] = 8'd255; + lut[32] = 8'd0; + lut[33] = 8'd7; + lut[34] = 8'd8; + lut[35] = 8'd10; + lut[36] = 8'd11; + lut[37] = 8'd12; + lut[38] = 8'd14; + lut[39] = 8'd15; + lut[40] = 8'd17; + lut[41] = 8'd20; + lut[42] = 8'd22; + lut[43] = 8'd25; + lut[44] = 8'd28; + lut[45] = 8'd31; + lut[46] = 8'd35; + lut[47] = 8'd40; + lut[48] = 8'd45; + lut[49] = 8'd50; + lut[50] = 8'd56; + lut[51] = 8'd63; + lut[52] = 8'd71; + lut[53] = 8'd80; + lut[54] = 8'd90; + lut[55] = 8'd101; + lut[56] = 8'd113; + lut[57] = 8'd127; + lut[58] = 8'd143; + lut[59] = 8'd160; + lut[60] = 8'd180; + lut[61] = 8'd202; + lut[62] = 8'd227; + lut[63] = 8'd255; + lut[64] = 8'd0; + lut[65] = 8'd18; + lut[66] = 8'd20; + lut[67] = 8'd22; + lut[68] = 8'd24; + lut[69] = 8'd26; + lut[70] = 8'd29; + lut[71] = 8'd31; + lut[72] = 8'd34; + lut[73] = 8'd37; + lut[74] = 8'd41; + lut[75] = 8'd45; + lut[76] = 8'd49; + lut[77] = 8'd53; + lut[78] = 8'd58; + lut[79] = 8'd63; + lut[80] = 8'd69; + lut[81] = 8'd75; + lut[82] = 8'd82; + lut[83] = 8'd90; + lut[84] = 8'd98; + lut[85] = 8'd107; + lut[86] = 8'd116; + lut[87] = 8'd127; + lut[88] = 8'd139; + lut[89] = 8'd151; + lut[90] = 8'd165; + lut[91] = 8'd180; + lut[92] = 8'd196; + lut[93] = 8'd214; + lut[94] = 8'd233; + lut[95] = 8'd255; + lut[96] = 8'd0; + lut[97] = 8'd51; + lut[98] = 8'd54; + lut[99] = 8'd57; + lut[100] = 8'd60; + lut[101] = 8'd63; + lut[102] = 8'd67; + lut[103] = 8'd70; + lut[104] = 8'd74; + lut[105] = 8'd78; + lut[106] = 8'd83; + lut[107] = 8'd87; + lut[108] = 8'd92; + lut[109] = 8'd97; + lut[110] = 8'd103; + lut[111] = 8'd108; + lut[112] = 8'd114; + lut[113] = 8'd120; + lut[114] = 8'd127; + lut[115] = 8'd134; + lut[116] = 8'd141; + lut[117] = 8'd149; + lut[118] = 8'd157; + lut[119] = 8'd166; + lut[120] = 8'd175; + lut[121] = 8'd185; + lut[122] = 8'd195; + lut[123] = 8'd206; + lut[124] = 8'd217; + lut[125] = 8'd229; + lut[126] = 8'd241; + lut[127] = 8'd255; + +end +endmodule + + +/* This file is part of JT49. + + JT49 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, either version 3 of the License, or + (at your option) any later version. + + JT49 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT49. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 10-Nov-2018 + + Based on sqmusic, by the same author + + */ + + +module jt49_noise( + (* direct_enable *) input cen, + input clk, + input rst_n, + input [4:0] period, + output reg noise +); + +reg [5:0]count; +reg [16:0]poly17; +wire poly17_zero = poly17==17'b0; +wire noise_en; +reg last_en; + +wire noise_up = noise_en && !last_en; + +always @(posedge clk ) if(cen) begin + noise <= ~poly17[0]; +end + +always @( posedge clk, negedge rst_n ) + if( !rst_n ) + poly17 <= 17'd0; + else if( cen ) begin + last_en <= noise_en; + if( noise_up ) + poly17 <= { poly17[0] ^ poly17[3] ^ poly17_zero, poly17[16:1] }; + end + +jt49_div #(5) u_div( + .clk ( clk ), + .cen ( cen ), + .rst_n ( rst_n ), + .period ( period ), + .div ( noise_en ) +); + +endmodule diff --git a/timing/resource/jt5205.v b/timing/resource/jt5205.v new file mode 100644 index 00000000..f565dfc2 --- /dev/null +++ b/timing/resource/jt5205.v @@ -0,0 +1,335 @@ +/* This file is part of JT5205. + JT5205 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, either version 3 of the License, or + (at your option) any later version. + + JT5205 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT5205. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 30-10-2019 */ + +module jt5205( + input rst, + input clk, + input cen /* direct_enable */, + input [ 1:0] sel, // s pin + input [ 3:0] din, + output signed [11:0] sound, + output sample, + // This output pin is not part of MSM5205 I/O + // It helps integrating the system as it produces + // a strobe + // at the internal clock divider pace + output irq, + output vclk_o + `ifdef JT5205_DEBUG + , + output signed [11:0] debug_raw, + output debug_cen_lo + `endif +); + +// Enabling the interpolator changes the sound of Chun Li's beat in +// SF2 too much. So I decided to disable it +parameter INTERPOL=0, // 1 for simple linear interpolation. 0 for raw output + VCLK_CEN=1; // 1 for using vclk_o as an output clock enable + // 0 for keeping vclk_o duty cycle as 50% + +wire cen_lo, cen_mid; +wire signed [11:0] raw; + +assign irq=cen_lo; // Notice that irq is active even if rst is high. This is + // important for games such as Tora e no michi. + +`ifdef JT5205_DEBUG +assign debug_raw = raw; +assign debug_cen_lo = cen_lo; +`endif + + +jt5205_timing #(VCLK_CEN) u_timing( + .clk ( clk ), + .cen ( cen ), + .sel ( sel ), + .cen_lo ( cen_lo ), + .cen_mid( cen_mid ), + .cenb_lo( ), + .vclk_o (vclk_o ) +); + +jt5205_adpcm u_adpcm( + .rst ( rst ), + .clk ( clk ), + .cen_lo ( cen_lo ), + .cen_hf ( cen ), + .din ( din ), + .sound ( raw ) +); + +generate + if( INTERPOL == 1 ) begin + jt5205_interpol2x u_interpol( + .rst ( rst ), + .clk ( clk ), + .cen_mid( cen_mid ), + .din ( raw ), + .dout ( sound ) + ); + assign sample=cen_mid; // 2x the original sampling freq. because of interpolator + end else begin + assign sound = raw; + assign sample = cen_lo; + end +endgenerate + + +endmodule + +/* This file is part of JT5205. + JT5205 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, either version 3 of the License, or + (at your option) any later version. + + JT5205 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT5205. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 30-10-2019 */ + +module jt5205_adpcm( + input rst, + input clk, + (* direct_enable *) input cen_hf, + (* direct_enable *) input cen_lo, + input [ 3:0] din, + output reg signed [11:0] sound +); + +reg [ 5:0] delta_idx, idx_inc; +reg [10:0] delta[0:48]; + +reg [11:0] dn; +reg [12:0] qn; +reg up; +reg [ 2:0] factor; +reg [ 3:0] din_copy; +reg [ 5:0] next_idx; +reg signed [13:0] unlim; + +`ifdef SIMULATION +initial begin + sound = -12'd2; +end +`endif + +always @(posedge clk, posedge rst) begin + if( rst ) begin + factor <= 3'd0; + up <= 1'b0; + next_idx <= 6'd0; + dn <= 12'd0; + qn <= 13'd0; + end else if(cen_hf) begin + up <= cen_lo; + if( up ) begin + factor <= din_copy[2:0]; + dn <= { 1'b0, delta[delta_idx] }; + qn <= { 2'd0, delta[delta_idx]>>3}; + next_idx <= din_copy[2] ? (delta_idx+idx_inc) : (delta_idx-6'd1); + end else begin + if(factor[2]) begin + qn <= qn + {1'b0, dn }; + end + dn <= dn>>1; + factor <= factor<<1; + if( next_idx>6'd48) + next_idx <= din_copy[2] ? 6'd48 : 6'd0; + end + end +end + +always @(posedge clk ) if(cen_lo) begin + if( rst ) begin + // sound fades away after a rst but the rest level must be -2 + // otherwise noises can be heard (e.g. intro scene of Double Dragon) + if( sound>12'd0 || sound < -12'd2 ) + sound <= sound >>> 1; + else + sound <= -12'd2; + end else begin + sound <= unlim[13:12]!={2{unlim[11]}} ? { unlim[13], {11{~unlim[13]}}} : unlim[11:0]; + end +end + +function signed [13:0] extend; + input signed [11:0] a; + extend = { {2{a[11]}}, a }; +endfunction + +always @(*) begin + unlim = din_copy[3] ? extend(sound) - {1'b0, qn} : + extend(sound) + {1'b0, qn}; +end + +always @(posedge clk, posedge rst) begin + if( rst ) begin + delta_idx <= 6'd0; + din_copy <= 4'd0; + end else if(cen_lo) begin + case( din[1:0] ) + 2'd0: idx_inc <= 6'd2; + 2'd1: idx_inc <= 6'd4; + 2'd2: idx_inc <= 6'd6; + 2'd3: idx_inc <= 6'd8; + endcase + din_copy <= din; + delta_idx <= next_idx; + end +end + +initial begin +delta[ 0] = 11'd0016; delta[ 1] = 11'd0017; delta[ 2] = 11'd0019; delta[ 3] = 11'd0021; delta[ 4] = 11'd0023; delta[ 5] = 11'd0025; delta[ 6] = 11'd0028; +delta[ 7] = 11'd0031; delta[ 8] = 11'd0034; delta[ 9] = 11'd0037; delta[10] = 11'd0041; delta[11] = 11'd0045; delta[12] = 11'd0050; delta[13] = 11'd0055; +delta[14] = 11'd0060; delta[15] = 11'd0066; delta[16] = 11'd0073; delta[17] = 11'd0080; delta[18] = 11'd0088; delta[19] = 11'd0097; delta[20] = 11'd0107; +delta[21] = 11'd0118; delta[22] = 11'd0130; delta[23] = 11'd0143; delta[24] = 11'd0157; delta[25] = 11'd0173; delta[26] = 11'd0190; delta[27] = 11'd0209; +delta[28] = 11'd0230; delta[29] = 11'd0253; delta[30] = 11'd0279; delta[31] = 11'd0307; delta[32] = 11'd0337; delta[33] = 11'd0371; delta[34] = 11'd0408; +delta[35] = 11'd0449; delta[36] = 11'd0494; delta[37] = 11'd0544; delta[38] = 11'd0598; delta[39] = 11'd0658; delta[40] = 11'd0724; delta[41] = 11'd0796; +delta[42] = 11'd0876; delta[43] = 11'd0963; delta[44] = 11'd1060; delta[45] = 11'd1166; delta[46] = 11'd1282; delta[47] = 11'd1411; delta[48] = 11'd1552; +end + +endmodule + + +/* This file is part of JT5205. + JT5205 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, either version 3 of the License, or + (at your option) any later version. + + JT5205 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT5205. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 30-12-2019 */ + +// Simple 2x interpolator +// Reduces HF content without altering too much the +// original sound + +module jt5205_interpol2x( + input rst, + input clk, + (* direct_enable *) input cen_mid, + input signed [11:0] din, + output reg signed [11:0] dout +); + +reg signed [11:0] last; + +always @(posedge clk, posedge rst) begin + if(rst) begin + last <= 12'd0; + dout <= 12'd0; + end else if(cen_mid) begin + last <= din; + dout <= (last>>>1)+(din>>>1); + end +end + +endmodule + + +/* This file is part of JT5205. + JT5205 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, either version 3 of the License, or + (at your option) any later version. + + JT5205 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT5205. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 30-10-2019 */ + +module jt5205_timing( + input clk, + (* direct_enable *) input cen, + input [ 1:0] sel, // s pin + output cen_lo, // sample rate + output cenb_lo, // sample rate (opposite phase) + output cen_mid, // 2x sample rate + output reg vclk_o +); + +parameter VCLK_CEN=1; + +reg [6:0] cnt=0; +reg pre=0, preb=0; +reg [6:0] lim; + +always @(posedge clk) begin + case(sel) + 0: lim <= 95; + 1: lim <= 63; + 2: lim <= 47; + 3: lim <= 1; + endcase +end + +always @(posedge clk) begin + if(sel==3) begin + cnt <= 0; + vclk_o <= 0; + end + if(cen) begin + if(sel!=3) cnt <= cnt + 7'd1; + + pre <= 0; + preb <= 0; + if(cnt==lim) begin + vclk_o <= 1; + cnt <= 0; + pre <= 1; + end + if(cnt==(lim>>1)) begin + preb <= 1; + vclk_o <= 0; + end + end else if(VCLK_CEN) begin + vclk_o <= 0; + end +end + +assign cen_lo = pre &cen; +assign cenb_lo = preb&cen; +assign cen_mid = (pre|preb)&cen; + +endmodule + diff --git a/timing/resource/jt7759.v b/timing/resource/jt7759.v new file mode 100644 index 00000000..01edad66 --- /dev/null +++ b/timing/resource/jt7759.v @@ -0,0 +1,815 @@ +/* This file is part of JT7759. + JT7759 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, either version 3 of the License, or + (at your option) any later version. + + JT7759 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT7759. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 5-7-2020 */ + +module jt7759( + input rst, + input clk, // Use same clock as sound CPU + input cen, // 640kHz + input stn, // STart (active low) + input cs, + input mdn, // MODE: 1 for stand alone mode, 0 for slave mode + // see chart in page 13 of PDF + output busyn, + // CPU interface + input wrn, // for slave mode only, 31.7us after drqn is set + input [ 7:0] din, + output drqn, // data request. 50-70us delay after mdn goes low + // ROM interface + output rom_cs, // equivalent to DRQn in original chip + output [16:0] rom_addr, + input [ 7:0] rom_data, + input rom_ok, + // Sound output + output signed [ 8:0] sound + +`ifdef DEBUG + ,output [3:0] debug_nibble + ,output debug_cen_dec + ,output debug_dec_rst +`endif +); + +wire [ 5:0] divby; +wire cen_dec; // internal clock enable for sound +wire cen_ctl; // cen_dec x4 + +wire dec_rst; +wire [ 3:0] encoded; + +wire ctrl_cs, ctrl_ok, ctrl_flush; +wire [16:0] ctrl_addr; +wire [ 7:0] ctrl_din; + + +`ifdef DEBUG + assign debug_nibble = encoded; + assign debug_cen_dec = cen_dec; + assign debug_dec_rst = dec_rst; +`endif + +jt7759_div u_div( + .clk ( clk ), + .cen ( cen ), + .cen_ctl ( cen_ctl ), + .divby ( divby ), + .cen_dec ( cen_dec ) +); + +jt7759_ctrl u_ctrl( + .rst ( rst ), + .clk ( clk ), + .cen_ctl ( cen_ctl ), + .cen_dec ( cen_dec ), + .divby ( divby ), + // chip interface + .stn ( stn ), + .cs ( cs ), + .mdn ( mdn ), + .drqn ( drqn ), + .busyn ( busyn ), + .wrn ( wrn ), + .din ( din ), + // ADPCM engine + .dec_rst ( dec_rst ), + .dec_din ( encoded ), + // ROM interface + .rom_cs ( ctrl_cs ), + .rom_addr ( ctrl_addr ), + .rom_data ( ctrl_din ), + .rom_ok ( ctrl_ok ), + .flush ( ctrl_flush) +); + +jt7759_data u_data( + .rst ( rst ), + .clk ( clk ), + .cen_ctl ( cen_ctl ), + .mdn ( mdn ), + // Control interface + .ctrl_flush ( ctrl_flush), + .ctrl_cs ( ctrl_cs ), + .ctrl_addr ( ctrl_addr ), + .ctrl_din ( ctrl_din ), + .ctrl_ok ( ctrl_ok ), + .ctrl_busyn ( busyn ), + // ROM interface + .rom_cs ( rom_cs ), + .rom_addr ( rom_addr ), + .rom_data ( rom_data ), + .rom_ok ( rom_ok ), + // Passive interface + .cs ( cs ), + .wrn ( wrn ), + .din ( din ), + .drqn ( drqn ) +); + +jt7759_adpcm u_adpcm( + .rst ( dec_rst ), + .clk ( clk ), + .cen_dec ( cen_dec ), + .encoded ( encoded ), + .sound ( sound ) +); + + +`ifdef SIMULATION +integer fsnd; +initial begin + fsnd=$fopen("jt7759.raw","wb"); +end +wire signed [15:0] snd_log = { sound, 7'b0 }; +always @(posedge cen_dec) begin + $fwrite(fsnd,"%u", {snd_log, snd_log}); +end +`endif +endmodule + +/* This file is part of JT7759. + JT7759 program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public Licen4se as published by + the Free Software Foundation, either version 3 of the Licen4se, or + (at your option) any later version. + + JT7759 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public Licen4se for more details. + + You should have received a copy of the GNU General Public Licen4se + along with JT7759. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 5-7-2020 */ + +module jt7759_adpcm #(parameter SW=9) ( + input rst, + input clk, + input cen_dec, + input [ 3:0] encoded, + output reg signed [SW-1:0] sound +); + +// The look-up table could have been compressed. One obvious way is to realize that one +// half of it is just the negative version of the other. +// However, because it will be synthesized as a 1 kilo word memory of 9-bit words, i.e. 1BRAM +// this is the best choice. +// This is generated by the file doc/lut.c + +reg signed [8:0] lut[0:255]; +reg signed [3:0] st_lut[0:7]; +reg [3:0] st; +reg [3:0] st_delta; +reg signed [5:0] st_next, st_sum; +reg signed [SW:0] next_snd, lut_step; + +function [SW:0] sign_ext; + input signed [8:0] din; + sign_ext = { {SW-8{din[8]}}, din }; +endfunction + +always @(*) begin + st_delta = st_lut[ encoded[2:0] ]; + st_sum = {2'b0, st } + {{2{st_delta[3]}}, st_delta }; + if( st_sum[5] ) + st_next = 6'd0; + else if( st_sum[4] ) + st_next = 6'd15; + else + st_next = st_sum; + lut_step = sign_ext( lut[{st,encoded}] ); + next_snd = { sound[SW-1], sound } + lut_step; +end + +always @(posedge clk, posedge rst ) begin + if( rst ) begin + sound <= {SW{1'd0}}; + st <= 4'd0; + end else if(cen_dec) begin + if( next_snd[SW]==next_snd[SW-1] ) + sound <= next_snd[SW-1:0]; + else sound <= next_snd[SW] ? {1'b1,{SW-1{1'b0}}} : {1'b0,{SW-1{1'b1}}}; + st <= st_next[3:0]; + end +end + +initial begin + st_lut[0]=-4'd1; st_lut[1]=-4'd1; st_lut[2]=4'd0; st_lut[3]=4'd0; + st_lut[4]=4'd1; st_lut[5]=4'd2; st_lut[6]=4'd2; st_lut[7]=4'd3; +end + + +initial begin + lut[8'h00]= 9'd0; lut[8'h01]= 9'd0; lut[8'h02]= 9'd1; lut[8'h03]= 9'd2; + lut[8'h04]= 9'd3; lut[8'h05]= 9'd5; lut[8'h06]= 9'd7; lut[8'h07]= 9'd10; + lut[8'h08]= 9'd0; lut[8'h09]= 9'd0; lut[8'h0A]=-9'd1; lut[8'h0B]=-9'd2; + lut[8'h0C]=-9'd3; lut[8'h0D]=-9'd5; lut[8'h0E]=-9'd7; lut[8'h0F]=-9'd10; + lut[8'h10]= 9'd0; lut[8'h11]= 9'd1; lut[8'h12]= 9'd2; lut[8'h13]= 9'd3; + lut[8'h14]= 9'd4; lut[8'h15]= 9'd6; lut[8'h16]= 9'd8; lut[8'h17]= 9'd13; + lut[8'h18]= 9'd0; lut[8'h19]=-9'd1; lut[8'h1A]=-9'd2; lut[8'h1B]=-9'd3; + lut[8'h1C]=-9'd4; lut[8'h1D]=-9'd6; lut[8'h1E]=-9'd8; lut[8'h1F]=-9'd13; + lut[8'h20]= 9'd0; lut[8'h21]= 9'd1; lut[8'h22]= 9'd2; lut[8'h23]= 9'd4; + lut[8'h24]= 9'd5; lut[8'h25]= 9'd7; lut[8'h26]= 9'd10; lut[8'h27]= 9'd15; + lut[8'h28]= 9'd0; lut[8'h29]=-9'd1; lut[8'h2A]=-9'd2; lut[8'h2B]=-9'd4; + lut[8'h2C]=-9'd5; lut[8'h2D]=-9'd7; lut[8'h2E]=-9'd10; lut[8'h2F]=-9'd15; + lut[8'h30]= 9'd0; lut[8'h31]= 9'd1; lut[8'h32]= 9'd3; lut[8'h33]= 9'd4; + lut[8'h34]= 9'd6; lut[8'h35]= 9'd9; lut[8'h36]= 9'd13; lut[8'h37]= 9'd19; + lut[8'h38]= 9'd0; lut[8'h39]=-9'd1; lut[8'h3A]=-9'd3; lut[8'h3B]=-9'd4; + lut[8'h3C]=-9'd6; lut[8'h3D]=-9'd9; lut[8'h3E]=-9'd13; lut[8'h3F]=-9'd19; + lut[8'h40]= 9'd0; lut[8'h41]= 9'd2; lut[8'h42]= 9'd3; lut[8'h43]= 9'd5; + lut[8'h44]= 9'd8; lut[8'h45]= 9'd11; lut[8'h46]= 9'd15; lut[8'h47]= 9'd23; + lut[8'h48]= 9'd0; lut[8'h49]=-9'd2; lut[8'h4A]=-9'd3; lut[8'h4B]=-9'd5; + lut[8'h4C]=-9'd8; lut[8'h4D]=-9'd11; lut[8'h4E]=-9'd15; lut[8'h4F]=-9'd23; + lut[8'h50]= 9'd0; lut[8'h51]= 9'd2; lut[8'h52]= 9'd4; lut[8'h53]= 9'd7; + lut[8'h54]= 9'd10; lut[8'h55]= 9'd14; lut[8'h56]= 9'd19; lut[8'h57]= 9'd29; + lut[8'h58]= 9'd0; lut[8'h59]=-9'd2; lut[8'h5A]=-9'd4; lut[8'h5B]=-9'd7; + lut[8'h5C]=-9'd10; lut[8'h5D]=-9'd14; lut[8'h5E]=-9'd19; lut[8'h5F]=-9'd29; + lut[8'h60]= 9'd0; lut[8'h61]= 9'd3; lut[8'h62]= 9'd5; lut[8'h63]= 9'd8; + lut[8'h64]= 9'd12; lut[8'h65]= 9'd16; lut[8'h66]= 9'd22; lut[8'h67]= 9'd33; + lut[8'h68]= 9'd0; lut[8'h69]=-9'd3; lut[8'h6A]=-9'd5; lut[8'h6B]=-9'd8; + lut[8'h6C]=-9'd12; lut[8'h6D]=-9'd16; lut[8'h6E]=-9'd22; lut[8'h6F]=-9'd33; + lut[8'h70]= 9'd1; lut[8'h71]= 9'd4; lut[8'h72]= 9'd7; lut[8'h73]= 9'd10; + lut[8'h74]= 9'd15; lut[8'h75]= 9'd20; lut[8'h76]= 9'd29; lut[8'h77]= 9'd43; + lut[8'h78]=-9'd1; lut[8'h79]=-9'd4; lut[8'h7A]=-9'd7; lut[8'h7B]=-9'd10; + lut[8'h7C]=-9'd15; lut[8'h7D]=-9'd20; lut[8'h7E]=-9'd29; lut[8'h7F]=-9'd43; + lut[8'h80]= 9'd1; lut[8'h81]= 9'd4; lut[8'h82]= 9'd8; lut[8'h83]= 9'd13; + lut[8'h84]= 9'd18; lut[8'h85]= 9'd25; lut[8'h86]= 9'd35; lut[8'h87]= 9'd53; + lut[8'h88]=-9'd1; lut[8'h89]=-9'd4; lut[8'h8A]=-9'd8; lut[8'h8B]=-9'd13; + lut[8'h8C]=-9'd18; lut[8'h8D]=-9'd25; lut[8'h8E]=-9'd35; lut[8'h8F]=-9'd53; + lut[8'h90]= 9'd1; lut[8'h91]= 9'd6; lut[8'h92]= 9'd10; lut[8'h93]= 9'd16; + lut[8'h94]= 9'd22; lut[8'h95]= 9'd31; lut[8'h96]= 9'd43; lut[8'h97]= 9'd64; + lut[8'h98]=-9'd1; lut[8'h99]=-9'd6; lut[8'h9A]=-9'd10; lut[8'h9B]=-9'd16; + lut[8'h9C]=-9'd22; lut[8'h9D]=-9'd31; lut[8'h9E]=-9'd43; lut[8'h9F]=-9'd64; + lut[8'hA0]= 9'd2; lut[8'hA1]= 9'd7; lut[8'hA2]= 9'd12; lut[8'hA3]= 9'd19; + lut[8'hA4]= 9'd27; lut[8'hA5]= 9'd37; lut[8'hA6]= 9'd51; lut[8'hA7]= 9'd76; + lut[8'hA8]=-9'd2; lut[8'hA9]=-9'd7; lut[8'hAA]=-9'd12; lut[8'hAB]=-9'd19; + lut[8'hAC]=-9'd27; lut[8'hAD]=-9'd37; lut[8'hAE]=-9'd51; lut[8'hAF]=-9'd76; + lut[8'hB0]= 9'd2; lut[8'hB1]= 9'd9; lut[8'hB2]= 9'd16; lut[8'hB3]= 9'd24; + lut[8'hB4]= 9'd34; lut[8'hB5]= 9'd46; lut[8'hB6]= 9'd64; lut[8'hB7]= 9'd96; + lut[8'hB8]=-9'd2; lut[8'hB9]=-9'd9; lut[8'hBA]=-9'd16; lut[8'hBB]=-9'd24; + lut[8'hBC]=-9'd34; lut[8'hBD]=-9'd46; lut[8'hBE]=-9'd64; lut[8'hBF]=-9'd96; + lut[8'hC0]= 9'd3; lut[8'hC1]= 9'd11; lut[8'hC2]= 9'd19; lut[8'hC3]= 9'd29; + lut[8'hC4]= 9'd41; lut[8'hC5]= 9'd57; lut[8'hC6]= 9'd79; lut[8'hC7]= 9'd117; + lut[8'hC8]=-9'd3; lut[8'hC9]=-9'd11; lut[8'hCA]=-9'd19; lut[8'hCB]=-9'd29; + lut[8'hCC]=-9'd41; lut[8'hCD]=-9'd57; lut[8'hCE]=-9'd79; lut[8'hCF]=-9'd117; + lut[8'hD0]= 9'd4; lut[8'hD1]= 9'd13; lut[8'hD2]= 9'd24; lut[8'hD3]= 9'd36; + lut[8'hD4]= 9'd50; lut[8'hD5]= 9'd69; lut[8'hD6]= 9'd96; lut[8'hD7]= 9'd143; + lut[8'hD8]=-9'd4; lut[8'hD9]=-9'd13; lut[8'hDA]=-9'd24; lut[8'hDB]=-9'd36; + lut[8'hDC]=-9'd50; lut[8'hDD]=-9'd69; lut[8'hDE]=-9'd96; lut[8'hDF]=-9'd143; + lut[8'hE0]= 9'd4; lut[8'hE1]= 9'd16; lut[8'hE2]= 9'd29; lut[8'hE3]= 9'd44; + lut[8'hE4]= 9'd62; lut[8'hE5]= 9'd85; lut[8'hE6]= 9'd118; lut[8'hE7]= 9'd175; + lut[8'hE8]=-9'd4; lut[8'hE9]=-9'd16; lut[8'hEA]=-9'd29; lut[8'hEB]=-9'd44; + lut[8'hEC]=-9'd62; lut[8'hED]=-9'd85; lut[8'hEE]=-9'd118; lut[8'hEF]=-9'd175; + lut[8'hF0]= 9'd6; lut[8'hF1]= 9'd20; lut[8'hF2]= 9'd36; lut[8'hF3]= 9'd54; + lut[8'hF4]= 9'd76; lut[8'hF5]= 9'd104; lut[8'hF6]= 9'd144; lut[8'hF7]= 9'd214; + lut[8'hF8]=-9'd6; lut[8'hF9]=-9'd20; lut[8'hFA]=-9'd36; lut[8'hFB]=-9'd54; + lut[8'hFC]=-9'd76; lut[8'hFD]=-9'd104; lut[8'hFE]=-9'd144; lut[8'hFF]=-9'd214; +end + +endmodule + +/* This file is part of JT7759. + JT7759 program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public Licen_ctlse as published by + the Free Software Foundation, either version 3 of the Licen_ctlse, or + (at your option) any later version. + + JT7759 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public Licen_ctlse for more details. + + You should have received a copy of the GNU General Public Licen_ctlse + along with JT7759. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 5-7-2020 */ + +module jt7759_ctrl( + input rst, + input clk, + input cen_ctl, + input cen_dec, + output reg [ 5:0] divby, + input stn, // STart (active low) + input cs, + input mdn, // MODE: 1 for stand alone mode, 0 for slave mode + input drqn, + output busyn, + // CPU interface + input wrn, // for slave mode only + input [ 7:0] din, + // ADPCM engine + output reg dec_rst, + output reg [ 3:0] dec_din, + // ROM interface + output rom_cs, // equivalent to DRQn in original chip + output reg [16:0] rom_addr, + input [ 7:0] rom_data, + input rom_ok, + output reg flush +); + +localparam STW = 12; +localparam [STW-1:0] RST = 12'd1<<0; // 1 +localparam [STW-1:0] IDLE = 12'd1<<1; // 2 +localparam [STW-1:0] SND_CNT= 12'd1<<2; // 4 +localparam [STW-1:0] PLAY = 12'd1<<3; // 8 +localparam [STW-1:0] WAIT = 12'd1<<4; // 10 +localparam [STW-1:0] GETN = 12'd1<<5; // 20 +localparam [STW-1:0] MUTED = 12'd1<<6; // 40 +localparam [STW-1:0] LOAD = 12'd1<<7; // 80 +localparam [STW-1:0] READCMD= 12'd1<<8; // 100 +localparam [STW-1:0] READADR= 12'd1<<9; // 200 +localparam [STW-1:0] SIGN = 12'd1<<10; // 400 +localparam [STW-1:0] DONE = 12'd1<<11; // 800 + +localparam MTB = 10, // base count at cen_ctl (1.56us) 1.5625us*512=0.8ms + MTW = 6+MTB; // Mute counter + +// reg [ 7:0] max_snd; // sound count: total number of sound samples +reg [STW-1:0] st; +reg [ 3:0] next; +reg [MTW-1:0] mute_cnt; +reg [ 8:0] data_cnt; +reg [ 3:0] rep_cnt; +reg [ 15:0] addr_latch; +// reg [ 16:0] rep_latch; +reg [ 7:0] sign[0:3]; +reg last_wr, getdiv, headerok; +// reg signok; // ROM signature ok +wire write, wr_posedge; +wire [ 16:0] next_rom; +wire [ 1:0] sign_addr = rom_addr[1:0]-2'd1; +reg pre_cs, pulse_cs; + +assign write = cs && ( (!mdn && !wrn) || !stn ); +assign wr_posedge = !last_wr && write; +assign busyn = st == IDLE || st == RST || st == DONE; +assign next_rom = rom_addr+1'b1; +assign rom_cs = pre_cs & ~pulse_cs; + +initial begin + sign[0] = 8'h5a; + sign[1] = 8'ha5; + sign[2] = 8'h69; + sign[3] = 8'h55; +end + +// Simulation log +`ifdef SIMULATION +`define JT7759_SILENCE $display("\tjt7759: silence"); +`define JT7759_PLAY $display("\tjt7759: play"); +`define JT7759_PLAY_LONG $display("\tjt7759: play n"); +`define JT7759_REPEAT $display("\tjt7759: repeat"); +`define JT7759_DONE $display("\tjt7759: sample done"); +`else +`define JT7759_SILENCE +`define JT7759_PLAY +`define JT7759_PLAY_LONG +`define JT7759_REPEAT +`define JT7759_DONE +`endif + + +always @(posedge clk, posedge rst) begin + if( rst ) begin + // max_snd <= 8'd0; + st <= RST; + pre_cs <= 0; + rom_addr <= 17'd0; + divby <= 6'h10; // ~8kHz + last_wr <= 0; + dec_rst <= 1; + dec_din <= 4'd0; + mute_cnt <= 0; + data_cnt <= 9'd0; + // signok <= 0; + rep_cnt <= ~4'd0; + // rep_latch <= 17'd0; + headerok <= 0; + addr_latch<= 0; + pulse_cs <= 0; + flush <= 0; + end else begin + last_wr <= write; + flush <= 0; + if( pulse_cs ) begin + /*if(cen_ctl)*/ pulse_cs <= 0; + end else begin + case( st ) + default: if(cen_ctl) begin // start up process + if( mdn ) begin + rom_addr <= 17'd0; + pre_cs <= 0; + dec_rst <= 1; + //pre_cs <= 1; + //st <= SND_CNT; // Reads the ROM header + st <= IDLE; + end + else st <= IDLE; + end + DONE: begin + st <= DONE; // stay here forever + flush <= 1; + end + // Check the chip signature + SIGN: if (cen_ctl) begin + if( rom_ok ) begin + if( !mdn ) begin + pulse_cs <= 1; + st <= READADR; + rom_addr[0] <= 1; + end else begin + if( rom_data != sign[sign_addr] ) begin + // signok <= 0; + st <= IDLE; + `ifdef SIMULATION + $display("Wrong ROM assigned to jt7759"); + $finish; + `endif + end + else begin + if( &sign_addr ) begin + // signok <= 1; + st<=IDLE; + end + rom_addr<= next_rom; + end + end + end + end + IDLE: begin + flush <= 1; + if( wr_posedge && drqn ) begin + //if( din <= max_snd || !mdn ) begin + pre_cs <= 1; + pulse_cs <= 1; + rom_addr <= { 7'd0, {1'd0, din} + 9'd2, 1'b1 }; + if( !mdn ) begin + st <= SIGN; + end else + st <= READADR; + //end + end else begin + if( rom_ok ) pre_cs <= 0; + dec_rst <= 1; + end + end + SND_CNT: begin + if( rom_ok ) begin + // max_snd <= rom_data; + rom_addr<= next_rom; + st <= SIGN; + end + end + READADR: if(cen_ctl) begin + if( rom_ok ) begin + if( rom_addr[0] ) begin + rom_addr <= next_rom; + pulse_cs <= 1; + addr_latch[ 7:0] <= rom_data; + end else begin + addr_latch[15:8] <= addr_latch[7:0]; + addr_latch[ 7:0] <= rom_data; + st <= LOAD; + if( mdn ) begin + pre_cs <= 0; + end else begin + pre_cs <= 1; + pulse_cs <= 1; + end + end + end + end + LOAD: if(cen_ctl) begin + if( mdn || rom_ok ) begin + rom_addr <= { addr_latch, 1'b1 }; + headerok <= 0; + st <= READCMD; + pre_cs <= 1; + pulse_cs <= 1; + rep_cnt <= ~4'd0; + if ( mdn ) flush <= 1; + end + end + READCMD: if(cen_ctl) begin + if( rom_ok ) begin + rom_addr <= next_rom; + pre_cs <= 1; + pulse_cs <= 1; + if( ~&rep_cnt ) begin + rep_cnt <= rep_cnt-1'd1; + end + + if(rom_data!=0) + headerok <= 1; + case( rom_data[7:6] ) + 2'd0: begin // Silence + `JT7759_SILENCE + mute_cnt <= {rom_data[5:0],{MTB{1'b0}}}; + if( rom_data==0 && headerok) begin + st <= DONE; + dec_rst <= 1; + `JT7759_DONE + end else begin + st <= MUTED; + end + pre_cs <= 0; + end + 2'd1: begin // 256 nibbles + data_cnt <= 9'hff; + divby <= rom_data[5:0]; + st <= PLAY; + `JT7759_PLAY + end + 2'd2: begin // n nibbles + data_cnt[8] <= 0; + divby <= rom_data[5:0]; + st <= GETN; + `JT7759_PLAY_LONG + end + 2'd3: begin // repeat loop + rep_cnt <= {1'b0, rom_data[2:0]}; + // rep_latch <= next_rom; + `JT7759_REPEAT + end + endcase + end + end + GETN: begin + if( rom_ok ) begin + rom_addr <= next_rom; + pre_cs <= 1; + pulse_cs <= 1; + data_cnt <= {1'b0, rom_data}; + st <= PLAY; + end + end + MUTED: if( cen_ctl ) begin + if(cen_dec) dec_rst<= 1; + if( mute_cnt != 0 ) begin + mute_cnt <= mute_cnt-1'd1; + end else begin + st <= READCMD; + pre_cs <= 1; + pulse_cs <= 1; + end + end + PLAY: begin + if(cen_dec) begin + if( pre_cs ) begin + if( rom_ok ) begin + { dec_din, next } <= rom_data; + dec_rst <= 0; + pre_cs <= 0; + data_cnt <= data_cnt-1'd1; + if( data_cnt==0 ) begin + pre_cs <= 1; + pulse_cs <= 1; + st <= READCMD; + end + end + end else begin + dec_din <= next; + rom_addr <= next_rom; + pre_cs <= 1; + pulse_cs <= 1; + data_cnt <= data_cnt-1'd1; + if( data_cnt==0 ) begin + st <= READCMD; + end + end + end + end + endcase + end // pulse_cen + end +end + + +endmodule + + +/* This file is part of JT7759. + JT7759 program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public Licen_ctlse as published by + the Free Software Foundation, either version 3 of the Licen_ctlse, or + (at your option) any later version. + + JT7759 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public Licen_ctlse for more details. + + You should have received a copy of the GNU General Public Licen_ctlse + along with JT7759. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 21-7-2021 */ + +module jt7759_data( + input rst, + input clk, + input cen_ctl, + input mdn, + // Control interface + input ctrl_flush, + input ctrl_cs, + input ctrl_busyn, + input [16:0] ctrl_addr, + output reg [ 7:0] ctrl_din, + output reg ctrl_ok, + // ROM interface + output rom_cs, + output reg [16:0] rom_addr, + input [ 7:0] rom_data, + input rom_ok, + // Passive interface + input cs, + input wrn, // for slave mode only + input [ 7:0] din, + output reg drqn +); + +reg [7:0] fifo[0:3]; +reg [3:0] fifo_ok; +reg drqn_l, ctrl_cs_l; +reg [1:0] rd_addr, wr_addr; +reg readin, readout, readin_l; +reg [4:0] drqn_cnt; + +wire good = mdn ? rom_ok & ~drqn_l & ~drqn : (cs&~wrn); +wire [7:0] din_mux = mdn ? rom_data : din; + +assign rom_cs = mdn && !drqn; + +always @(posedge clk, posedge rst) begin + if( rst ) begin + drqn_cnt <= 0; + end else begin + // Minimum time between DRQn pulses + if( readin || good ) + drqn_cnt <= ~'d0; + else if( drqn_cnt!=0 && cen_ctl) drqn_cnt <= drqn_cnt-1'd1; + end +end + +always @(posedge clk, posedge rst) begin + if( rst ) begin + rom_addr <= 0; + drqn <= 1; + readin_l <= 0; + end else begin + readin_l <= readin; + + if( !ctrl_busyn ) begin + if(!readin && readin_l) + rom_addr <= rom_addr + 1'd1; + if(fifo_ok==4'hf || (!readin && readin_l) ) begin + drqn <= 1; + end else if(fifo_ok!=4'hf && !readin && drqn_cnt==0 ) begin + drqn <= 0; + end + end else begin + drqn <= 1; + end + + if( ctrl_flush ) + rom_addr <= ctrl_addr; + end +end + +always @(posedge clk, posedge rst) begin + if( rst ) begin + rd_addr <= 0; + ctrl_cs_l <= 0; + readin <= 0; + readout <= 0; + ctrl_ok <= 0; + fifo_ok <= 0; + wr_addr <= 0; + drqn_l <= 1; + end else begin + ctrl_cs_l <= ctrl_cs; + drqn_l <= drqn; + + // read out + if( ctrl_cs && !ctrl_cs_l ) begin + readout <= 1; + ctrl_ok <= 0; + end + if( readout && fifo_ok[rd_addr] ) begin + ctrl_din <= fifo[ rd_addr ]; + ctrl_ok <= 1; + rd_addr <= rd_addr + 1'd1; + fifo_ok[ rd_addr ] <= 0; + readout <= 0; + end + if( !ctrl_cs ) begin + readout <= 0; + ctrl_ok <= 0; + end + + // read in + if( !drqn && drqn_l ) begin + readin <= 1; + end + if( good && readin ) begin + fifo[ wr_addr ] <= din_mux; + fifo_ok[ wr_addr ] <= 1; + wr_addr <= wr_addr + 1'd1; + readin <= 0; + `ifdef JT7759_FIFO_DUMP + $display("\tjt7759: read %X",din_mux); + `endif + end + + if( ctrl_busyn || ctrl_flush ) begin + fifo_ok <= 0; + rd_addr <= 0; + wr_addr <= 0; + end + end +end + +endmodule + + +/* This file is part of JT7759. + JT7759 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, either version 3 of the License, or + (at your option) any later version. + + JT7759 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JT7759. If not, see . + + Author: Jose Tejada Gomez. Twitter: @topapate + Version: 1.0 + Date: 5-7-2020 */ + +module jt7759_div( + input clk, + input cen, // 640kHz + input [5:0] divby, + output reg cen_ctl, // control = 8x faster than decoder + output reg cen_dec +); + +reg [1:0] cnt4; +reg [5:0] decdiv, /*ctldiv,*/ divby_l; +wire /*eoc_ctl,*/ eoc_dec, eoc_cnt; // end of count + +// assign eoc_ctl = ctldiv == { 1'b0, divby_l[5:1] }; +assign eoc_dec = decdiv == divby_l; +assign eoc_cnt = &cnt4; + +`ifdef SIMULATION +initial begin + cnt4 = 2'd0; + divby_l= 0; + decdiv = 6'd3; // bad start numbers to show the auto allignment feature + //ctldiv = 6'd7; +end +`endif + +always @(posedge clk) if(cen) begin + cnt4 <= cnt4+2'd1; + if( eoc_cnt ) begin + decdiv <= eoc_dec ? 6'd0 : (decdiv+1'd1); + if( eoc_dec ) divby_l <= divby < 6'd9 ? 6'd9 : divby; // The divider is updated only at EOC + end + // ctldiv <= eoc_ctl || (eoc_dec && eoc_cnt) ? 6'd0 : (ctldiv+1'd1); +end + +always @(posedge clk) begin + cen_ctl <= cen; // && eoc_ctl; + cen_dec <= cen && eoc_dec && eoc_cnt; +end + +endmodule + diff --git a/timing/resource/picorv32_large.v b/timing/resource/picorv32_large.v index 5ad56e18..cc6dab95 100644 --- a/timing/resource/picorv32_large.v +++ b/timing/resource/picorv32_large.v @@ -27,6 +27,7 @@ module top_large ( .BARREL_SHIFTER(1), .ENABLE_PCPI(1), .ENABLE_MUL(1), + .ENABLE_DIV(1), .ENABLE_IRQ(1) ) picorv32 ( .clk (clk ),