mirror of
https://github.com/MiSTer-devel/MacPlus_MiSTer.git
synced 2025-01-14 13:31:54 +00:00
284 lines
6.1 KiB
Systemverilog
284 lines
6.1 KiB
Systemverilog
//
|
|
// Communication module to MT32-pi (external MIDI emulator on RPi)
|
|
// (C) 2020 Sorgelig, Kitrinx
|
|
//
|
|
// https://github.com/dwhinham/mt32-pi
|
|
//
|
|
|
|
module mt32pi
|
|
(
|
|
input CLK_AUDIO,
|
|
|
|
input CLK_VIDEO,
|
|
input CE_PIXEL,
|
|
input VGA_VS,
|
|
input VGA_DE,
|
|
|
|
input [6:0] USER_IN,
|
|
output [6:0] USER_OUT,
|
|
|
|
input reset,
|
|
input midi_tx,
|
|
output midi_rx,
|
|
|
|
output reg [15:0] mt32_i2s_r,
|
|
output reg [15:0] mt32_i2s_l,
|
|
|
|
output reg mt32_available,
|
|
|
|
input mt32_mode_req,
|
|
input [1:0] mt32_rom_req,
|
|
input [7:0] mt32_sf_req,
|
|
|
|
output reg [7:0] mt32_mode,
|
|
output reg [7:0] mt32_rom,
|
|
output reg [7:0] mt32_sf,
|
|
output reg mt32_newmode,
|
|
|
|
output reg mt32_lcd_en,
|
|
output reg mt32_lcd_pix,
|
|
output reg mt32_lcd_update
|
|
);
|
|
|
|
//
|
|
// Pin | USB Name | Signal
|
|
// ----+----------+--------------
|
|
// 0 | D+ | I/O I2C_SDA / RX (midi in)
|
|
// 1 | D- | O TX (midi out)
|
|
// 2 | TX- | I I2S_WS (1 == right)
|
|
// 3 | GND_d | I I2C_SCL
|
|
// 4 | RX+ | I I2S_BCLK
|
|
// 5 | RX- | I I2S_DAT
|
|
// 6 | TX+ | - none
|
|
//
|
|
|
|
assign USER_OUT[0] = sda_out;
|
|
assign USER_OUT[1] = midi_tx;
|
|
assign USER_OUT[6:2] = '1;
|
|
|
|
|
|
//
|
|
// crossed/straight cable selection
|
|
//
|
|
|
|
generate
|
|
genvar i;
|
|
for(i = 0; i<2; i++) begin : clk_rate
|
|
wire clk_in = i ? USER_IN[6] : USER_IN[4];
|
|
reg [4:0] cnt;
|
|
always @(posedge CLK_AUDIO) begin : clkr
|
|
reg clk_sr, clk, old_clk;
|
|
reg [4:0] cnt_tmp;
|
|
|
|
clk_sr <= clk_in;
|
|
if (clk_sr == clk_in) clk <= clk_sr;
|
|
|
|
if(~&cnt_tmp) cnt_tmp <= cnt_tmp + 1'd1;
|
|
else cnt <= '1;
|
|
|
|
old_clk <= clk;
|
|
if(~old_clk & clk) begin
|
|
cnt <= cnt_tmp;
|
|
cnt_tmp <= 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
reg crossed;
|
|
always @(posedge CLK_AUDIO) crossed <= (clk_rate[0].cnt <= clk_rate[1].cnt);
|
|
endgenerate
|
|
|
|
wire i2s_ws = crossed ? USER_IN[2] : USER_IN[5];
|
|
wire i2s_data = crossed ? USER_IN[5] : USER_IN[2];
|
|
wire i2s_bclk = crossed ? USER_IN[4] : USER_IN[6];
|
|
assign midi_rx = ~mt32_available ? USER_IN[0] : crossed ? USER_IN[6] : USER_IN[4];
|
|
|
|
|
|
//
|
|
// i2s receiver
|
|
//
|
|
|
|
always @(posedge CLK_AUDIO) begin : i2s_proc
|
|
reg [15:0] i2s_buf = 0;
|
|
reg [4:0] i2s_cnt = 0;
|
|
reg clk_sr;
|
|
reg i2s_clk = 0;
|
|
reg old_clk, old_ws;
|
|
reg i2s_next = 0;
|
|
|
|
// Debounce clock
|
|
clk_sr <= i2s_bclk;
|
|
if (clk_sr == i2s_bclk) i2s_clk <= clk_sr;
|
|
|
|
// Latch data and ws on rising edge
|
|
old_clk <= i2s_clk;
|
|
if (i2s_clk && ~old_clk) begin
|
|
|
|
if (~i2s_cnt[4]) begin
|
|
i2s_cnt <= i2s_cnt + 1'd1;
|
|
i2s_buf[~i2s_cnt[3:0]] <= i2s_data;
|
|
end
|
|
|
|
// Word Select will change 1 clock before the new word starts
|
|
old_ws <= i2s_ws;
|
|
if (old_ws != i2s_ws) i2s_next <= 1;
|
|
end
|
|
|
|
if (i2s_next) begin
|
|
i2s_next <= 0;
|
|
i2s_cnt <= 0;
|
|
i2s_buf <= 0;
|
|
|
|
if (i2s_ws) mt32_i2s_l <= i2s_buf;
|
|
else mt32_i2s_r <= i2s_buf;
|
|
end
|
|
|
|
if (reset) begin
|
|
i2s_buf <= 0;
|
|
mt32_i2s_l <= 0;
|
|
mt32_i2s_r <= 0;
|
|
end
|
|
end
|
|
|
|
|
|
//
|
|
// i2c slave
|
|
//
|
|
|
|
reg sda_out;
|
|
reg [7:0] lcd_data[1024];
|
|
reg lcd_sz;
|
|
|
|
reg reset_r = 0;
|
|
wire [7:0] mode_req = reset_r ? 8'hA0 : mt32_mode_req ? 8'hA2 : 8'hA1;
|
|
wire [7:0] rom_req = {6'd0, mt32_rom_req};
|
|
|
|
always @(posedge CLK_AUDIO) begin : i2c_slave
|
|
reg sda_sr, scl_sr;
|
|
reg old_sda, old_scl;
|
|
reg sda, scl;
|
|
reg [7:0] tmp;
|
|
reg [3:0] cnt = 0;
|
|
reg [10:0] bcnt = 0;
|
|
reg ack;
|
|
reg i2c_rw;
|
|
reg disp, dispdata;
|
|
reg [2:0] div;
|
|
reg old_reset;
|
|
|
|
old_reset <= reset;
|
|
if(old_reset & ~reset) sda_out <= 1;
|
|
|
|
div <= div + 1'd1;
|
|
if(!div) begin
|
|
sda_sr <= USER_IN[0];
|
|
if(sda_sr == USER_IN[0]) sda <= sda_sr;
|
|
old_sda <= sda;
|
|
|
|
scl_sr <= USER_IN[3];
|
|
if(scl_sr == USER_IN[3]) scl <= scl_sr;
|
|
old_scl <= scl;
|
|
|
|
//start
|
|
if(old_scl & scl & old_sda & ~sda) begin
|
|
cnt <= 9;
|
|
bcnt <= 0;
|
|
ack <= 0;
|
|
i2c_rw <= 0;
|
|
disp <= 0;
|
|
dispdata <= 0;
|
|
end
|
|
|
|
//stop
|
|
if(old_scl & scl & ~old_sda & sda) begin
|
|
cnt <= 0;
|
|
if(dispdata) begin
|
|
lcd_sz <= ~bcnt[9];
|
|
mt32_lcd_update <= ~mt32_lcd_update;
|
|
end
|
|
end
|
|
|
|
//data latch
|
|
if(~old_scl && scl && cnt) begin
|
|
tmp <= {tmp[6:0], sda};
|
|
cnt <= cnt - 1'd1;
|
|
end
|
|
|
|
if(!cnt) sda_out <= 1;
|
|
|
|
//data set
|
|
if(old_scl && ~scl) begin
|
|
sda_out <= 1;
|
|
if(cnt == 1) begin
|
|
if(!bcnt) begin
|
|
if(tmp[7:1] == 'h45 || tmp[7:1] == 'h3c) begin
|
|
disp <= (tmp[7:1] == 'h3c);
|
|
sda_out <= 0;
|
|
mt32_available <= 1;
|
|
ack <= 1;
|
|
i2c_rw <= tmp[0];
|
|
bcnt <= bcnt + 1'd1;
|
|
cnt <= 10;
|
|
end
|
|
else begin
|
|
// wrong address, stop
|
|
cnt <= 0;
|
|
end
|
|
end
|
|
else if(ack) begin
|
|
if(~i2c_rw) begin
|
|
if(disp) begin
|
|
if(bcnt == 1) dispdata <= (tmp[7:6] == 2'b01);
|
|
else if(dispdata) lcd_data[bcnt[9:0] - 2'd2] <= tmp;
|
|
end
|
|
else begin
|
|
if(bcnt == 1) mt32_mode <= tmp;
|
|
if(bcnt == 2) mt32_rom <= tmp;
|
|
if(bcnt == 3) mt32_sf <= tmp;
|
|
if(bcnt == 3) mt32_newmode <= ~mt32_newmode;
|
|
end
|
|
end
|
|
if(~&bcnt) bcnt <= bcnt + 1'd1;
|
|
sda_out <= 0;
|
|
cnt <= 10;
|
|
end
|
|
end
|
|
else if(i2c_rw && ack && cnt && ~disp) begin
|
|
if(bcnt == 1) sda_out <= mode_req[cnt[2:0] - 2'd2];
|
|
if(bcnt == 2) sda_out <= rom_req[cnt[2:0] - 2'd2];
|
|
if(bcnt == 3) sda_out <= mt32_sf_req[cnt[2:0] - 2'd2];
|
|
if(bcnt == 3) reset_r <= 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
if(reset) begin
|
|
reset_r <= 1;
|
|
mt32_available <= 0;
|
|
end
|
|
end
|
|
|
|
always @(posedge CLK_VIDEO) begin
|
|
reg old_de, old_vs;
|
|
reg [7:0] hcnt;
|
|
reg [6:0] vcnt;
|
|
reg [7:0] sh;
|
|
|
|
if(CE_PIXEL) begin
|
|
old_de <= VGA_DE;
|
|
old_vs <= VGA_VS;
|
|
|
|
if(~&hcnt) hcnt <= hcnt + 1'd1;
|
|
sh <= (sh << 1) | (~old_de & VGA_DE);
|
|
if(sh[7]) hcnt <= 0;
|
|
|
|
if(old_de & ~VGA_DE & ~&vcnt) vcnt <= vcnt + 1'd1;
|
|
if(~old_vs & VGA_VS) vcnt <= 0;
|
|
|
|
mt32_lcd_en <= mt32_available & ~hcnt[7] && (lcd_sz ? !vcnt[6] : !vcnt[6:5]);
|
|
mt32_lcd_pix <= lcd_data[{vcnt[5:3],hcnt[6:0]}][vcnt[2:0]];
|
|
end
|
|
end
|
|
|
|
endmodule
|