// part of NeoGS project
//
// (c) NedoPC 2007-2019
module chan_ctrl
(
input wire clk, // 24.0 MHz
input wire rst_n,
// memory interface
output reg [ 6:0] rd_addr,
input wire [31:0] rd_data,
//
output reg [ 6:0] wr_addr,
output wire [31:0] wr_data,
output reg wr_stb,
// 37500 Hz period strobe (1-cycle strobe)
input wire sync_stb,
// channel enables
input wire [31:0] ch_enas,
// output data
output reg [ 7:0] out_data,
output reg out_stb_addr, // strobes address sequence (addrhi/mid/lo)
output reg out_stb_mix // strobes mix sequence (frac/vl/vr)
// sequence: addrhi, addrmid, addrlo; frac, vl, vr (6 bytes)
);
reg [ 5:0] curr_ch; // current channel number
wire stop = curr_ch[5];
// channel fetch state machine
reg [3:0] st;
reg [3:0] next_st;
// channel enable
wire ch_ena = ch_enas[curr_ch[4:0]];
// offset storage
reg [31:0] offset;
reg off_cy; // extra carry [32th bit]
// offset>=size flag
reg oversize;
// volumes storage
reg [5:0] vol_left;
reg [5:0] vol_right;
// miscellaneous
reg loopena;
reg surround;
// base address
reg [21:0] base;
// emit control
reg [1:0] addr_emit;
///////////////////////
// states definition //
///////////////////////
localparam ST_BEGIN = 4'd0;
localparam ST_GETOFFS = 4'd1; // when offset value arrives
localparam ST_GETADDVOL = 4'd2; // whed add and volumes arrive
localparam ST_GETSIZE = 4'd3; // size and part of base address arrive
localparam ST_GETLOOP = 4'd4; // when loop and last part of base address arrive
localparam ST_SAVEOFFS = 4'd5;
//localparam ST_ = 4'd;
//localparam ST_ = 4'd;
//localparam ST_ = 4'd;
//localparam ST_ = 4'd;
//localparam ST_ = 4'd;
localparam ST_NEXT = 4'd14;
localparam ST_WAIT = 4'd15;
always @(posedge clk)
if( st==ST_WAIT )
curr_ch[5:0] <= 6'd0;
else if( st==ST_NEXT )
curr_ch[5:0] <= curr_ch[5:0] + 6'd1;
always @(posedge clk, negedge rst_n)
if( !rst_n )
st <= ST_WAIT;
else
st <= next_st;
//
always @*
case( st )
//////////////////////////////////////////////////////////////////////
ST_BEGIN:
if( stop )
next_st = ST_WAIT;
else if( !ch_ena )
next_st = ST_NEXT;
else
next_st = ST_GETOFFS;
///////////////////////////////////////////////////////////////////////
ST_GETOFFS:
next_st = ST_GETADDVOL;
///////////////////////////////////////////////////////////////////////
ST_GETADDVOL:
next_st = ST_GETSIZE;
///////////////////////////////////////////////////////////////////////
ST_GETSIZE:
next_st = ST_GETLOOP;
///////////////////////////////////////////////////////////////////////
ST_GETLOOP:
next_st = ST_SAVEOFFS;
///////////////////////////////////////////////////////////////////////
ST_SAVEOFFS:
next_st = ST_NEXT;
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
ST_NEXT:
next_st = ST_BEGIN;
///////////////////////////////////////////////////////////////////////
ST_WAIT:
if( sync_stb )
next_st = ST_BEGIN;
else
next_st = ST_WAIT;
////////////////////////////////////////////////
///////////////////////
default: next_st = ST_WAIT;
///////////////////////////////////////////////////////////////////////
endcase
// state memory address control
always @*
rd_addr[6:2] <= curr_ch[4:0];
always @*
wr_addr[6:2] <= curr_ch[4:0];
always @(posedge clk)
wr_addr[1:0] <= 2'd0;
//
always @(posedge clk)
if( st==ST_NEXT || st==ST_WAIT )
begin
rd_addr[1:0] <= 2'd0;
end
else if( st==ST_BEGIN || st==ST_GETOFFS || st==ST_GETADDVOL )
begin
rd_addr[1:0] <= rd_addr[1:0] + 2'd1;
end
// offset register control
always @(posedge clk)
if( st==ST_GETOFFS )
offset <= rd_data;
else if( st==ST_GETADDVOL )
{off_cy, offset} <= {1'b0, offset} + {1'b0, 14'd0, rd_data[31:14]};
else if( st==ST_GETLOOP )
offset[31:12] <= oversize ? (offset[31:12]+rd_data[27:8]) : offset[31:12]; // TODO: or maybe rd_data & {20{oversize}} ?
// offset overflow control
always @(posedge clk)
if( st==ST_GETSIZE )
oversize <= ( {off_cy,offset[31:12]} >= {1'b0, rd_data[27:8]} );
// offset writeback
always @(posedge clk)
wr_stb <= st==ST_SAVEOFFS;
//
assign wr_data = offset;
// volumes and miscellaneous
always @(posedge clk)
if( st==ST_GETADDVOL )
begin
vol_left <= rd_data[11:6];
vol_right <= rd_data[ 5:0];
loopena <= rd_data[13];
surround <= rd_data[12];
end
// base address calc
always @(posedge clk)
if( st==ST_GETSIZE )
base[15:8] <= rd_data[7:0];
else if( st==ST_GETLOOP )
base[21:16] <= rd_data[5:0];
else if( st==ST_SAVEOFFS )
begin
base[7:0] <= offset[19:12];
base[21:8] <= base[21:8] + {2'd0,offset[31:20]};
end
// emitting data to fifos
always @(posedge clk, negedge rst_n)
if( !rst_n )
addr_emit <= 2'd0;
else
addr_emit[1:0] <= {addr_emit[0], st==ST_NEXT};
//
always @(posedge clk)
if( st==ST_GETSIZE )
out_data <= offset[11:4];
else if( st==ST_GETLOOP )
out_data <= {2'd0, vol_left[5:0]};
else if( st==ST_SAVEOFFS )
out_data <= {2'd0, vol_right[5:0] ^ {6{surround}}};
else if( st==ST_NEXT )
out_data <= {2'd0, base[21:16]};
else if( addr_emit[0] )
out_data <= base[15:8];
else
out_data <= base[7:0];
//
always @(posedge clk, negedge rst_n)
if( !rst_n )
out_stb_mix <= 1'b0;
else
out_stb_mix <= (st==ST_GETSIZE) ||
(st==ST_GETLOOP) ||
(st==ST_SAVEOFFS) ;
//
always @(posedge clk, negedge rst_n)
if( !rst_n )
out_stb_addr <= 1'b0;
else
out_stb_addr <= (st==ST_NEXT) || addr_emit;
endmodule