mirror of
https://github.com/trcwm/Speech256.git
synced 2025-06-07 16:48:32 +02:00
367 lines
11 KiB
Verilog
367 lines
11 KiB
Verilog
//
|
|
// Speech256 controller / sequencer
|
|
//
|
|
// Niels Moseley - Moseley Instruments 2017
|
|
// http://www.moseleyinstruments.com
|
|
//
|
|
//
|
|
|
|
module CONTROLLER (
|
|
clk, // global Speech256 clock
|
|
rst_an,
|
|
ldq, // load request, is high when new allophone can be loaded
|
|
data_in, // allophone input
|
|
data_stb, // allophone strobe input
|
|
period_out, // pitch period output
|
|
amp_out, // amplitude output
|
|
coeff_out, // 8-bit coefficient data out
|
|
coeff_stb, // '1' when coeff_out holds new coefficient data
|
|
period_done_in // should be '1' when the source finished a period
|
|
);
|
|
|
|
//////////// CLOCK //////////
|
|
input clk;
|
|
|
|
//////////// RESET, ACTIVE LOW //////////
|
|
input rst_an;
|
|
|
|
//////////// OUTPUTS //////////
|
|
output reg ldq;
|
|
output reg coeff_stb;
|
|
output reg signed [9:0] coeff_out;
|
|
output reg [15:0] amp_out;
|
|
output reg [7:0] period_out;
|
|
//output reg [7:0] dur_out;
|
|
|
|
//////////// INPUTS //////////
|
|
input [5:0] data_in;
|
|
input data_stb;
|
|
input source_stb_in;
|
|
input period_done_in; // used for duration counting
|
|
|
|
// internal counter and data registers
|
|
wire [7:0] rom_data;
|
|
reg [11:0] rom_addr;
|
|
reg [11:0] last_rom_addr;
|
|
reg [2:0] rom_addr_sel;
|
|
reg rom_en;
|
|
|
|
reg [3:0] jmpmsb;
|
|
reg jmpmsb_load;
|
|
|
|
reg [5:0] cur_allo;
|
|
reg [3:0] cur_cmd;
|
|
|
|
reg [3:0] cur_state;
|
|
reg [3:0] next_state;
|
|
|
|
reg [5:0] data_in_buf;
|
|
|
|
reg amp1_load, amp2_load;
|
|
reg period_load;
|
|
reg dur_load;
|
|
reg allo_load;
|
|
reg load_cur_cmd;
|
|
reg serve_pitch_data;
|
|
reg set_ldq, reset_ldq;
|
|
reg dur_cnt_clear;
|
|
wire serve_next_allo_data;
|
|
|
|
reg [15:0] amp_tmp;
|
|
reg [7:0] period_tmp;
|
|
reg [7:0] dur_tmp;
|
|
reg [7:0] duration;
|
|
reg [7:0] dur_cnt;
|
|
|
|
reg [2:0] coeff_cnt;
|
|
reg [1:0] coeff_cnt_update;
|
|
|
|
wire [9:0] coeff10bit;
|
|
|
|
wire done;
|
|
|
|
// control program ROM
|
|
CTRLROM u_ctrlrom (
|
|
.clk (clk),
|
|
.data (rom_data),
|
|
.addr (rom_addr),
|
|
.en (rom_en)
|
|
);
|
|
|
|
parameter ROM_ADDR_ZERO = 3'b000, // zero the ROM address
|
|
ROM_ADDR_INC = 3'b001, // increment the ROM address
|
|
ROM_ADDR_JMP = 3'b010, // jump to code
|
|
ROM_ADDR_ALLO = 3'b011, // read allophone jump address
|
|
ROM_ADDR_NOP = 3'b100;
|
|
|
|
parameter COEFF_CNT_ZERO = 2'b00, // zero coefficient counter
|
|
COEFF_CNT_NOP = 2'b01, // do nothing
|
|
COEFF_CNT_INC = 2'b10; // increment coefficient counter
|
|
|
|
// 8-bit -> 10-bit coefficient expander
|
|
XLAT u_xlat (
|
|
.c8_in(rom_data),
|
|
.c10_out(coeff10bit)
|
|
);
|
|
|
|
always @(posedge clk, negedge rst_an)
|
|
begin
|
|
if (rst_an == 0)
|
|
begin
|
|
// reset values
|
|
amp_out <= 0;
|
|
period_out <= 8'd1;
|
|
rom_en <= 1;
|
|
rom_addr <= 0;
|
|
last_rom_addr <= 0;
|
|
cur_state <= 0;
|
|
cur_allo <= 0;
|
|
cur_cmd <= 0;
|
|
coeff_stb <= 0;
|
|
coeff_out <= 0;
|
|
coeff_cnt <= 0;
|
|
jmpmsb <= 0;
|
|
ldq <= 0;
|
|
dur_cnt <= 0;
|
|
duration <= 0;
|
|
end
|
|
else
|
|
begin
|
|
// clocked process
|
|
cur_state <= next_state;
|
|
|
|
case (coeff_cnt_update)
|
|
COEFF_CNT_ZERO:
|
|
coeff_cnt <= 0;
|
|
COEFF_CNT_NOP:
|
|
coeff_cnt <= coeff_cnt;
|
|
COEFF_CNT_INC:
|
|
coeff_cnt <= coeff_cnt + 1;
|
|
default:
|
|
coeff_cnt <= 0;
|
|
endcase
|
|
|
|
if (jmpmsb_load)
|
|
jmpmsb <= rom_data[3:0];
|
|
|
|
if (amp1_load)
|
|
amp_tmp[7:0] <= rom_data;
|
|
|
|
if (amp2_load)
|
|
amp_tmp[15:8] <= rom_data;
|
|
|
|
if (period_load)
|
|
period_tmp <= rom_data;
|
|
|
|
if (dur_load)
|
|
dur_tmp <= rom_data;
|
|
|
|
if (allo_load)
|
|
cur_allo <= data_in;
|
|
|
|
if (load_cur_cmd)
|
|
cur_cmd <= rom_data[3:0];
|
|
|
|
if (serve_pitch_data)
|
|
begin
|
|
duration <= dur_tmp;
|
|
amp_out <= {4'b0000, amp_tmp[15:4]};
|
|
period_out <= period_tmp;
|
|
end
|
|
|
|
if (set_ldq == 1)
|
|
ldq <= 1;
|
|
else if (reset_ldq == 1)
|
|
ldq <= 0;
|
|
|
|
if ((period_done_in == 1) && (!serve_next_allo_data))
|
|
begin
|
|
dur_cnt <= dur_cnt + 1;
|
|
end
|
|
|
|
if (dur_cnt_clear)
|
|
begin
|
|
dur_cnt <= 0;
|
|
end
|
|
|
|
last_rom_addr <= rom_addr;
|
|
end
|
|
end
|
|
|
|
assign serve_next_allo_data = (dur_cnt == duration) ? 1 : 0;
|
|
|
|
parameter S_IDLE = 4'd0,
|
|
S_JMPADDR1 = 4'd1,
|
|
S_JMPADDR2 = 4'd2,
|
|
S_JMPADDR3 = 4'd3,
|
|
S_CMDLOAD = 4'd4,
|
|
S_CMDDECODE= 4'd5,
|
|
S_LOADAMP1 = 4'd6,
|
|
S_LOADAMP2 = 4'd7,
|
|
S_LOADDUR = 4'd8,
|
|
S_LOADPERIOD= 4'd9,
|
|
S_SERVEGATE = 4'd10,
|
|
S_LOADCOEF1 = 4'd11,
|
|
S_LOADCOEF2 = 4'd12;
|
|
|
|
always @(*)
|
|
begin
|
|
// rom address multiplexer
|
|
case (rom_addr_sel)
|
|
ROM_ADDR_ZERO:
|
|
rom_addr <= 0;
|
|
ROM_ADDR_INC:
|
|
rom_addr <= last_rom_addr + 1;
|
|
ROM_ADDR_JMP:
|
|
rom_addr <= {jmpmsb, rom_data[7:0]};
|
|
ROM_ADDR_ALLO:
|
|
rom_addr <= {5'b00000, cur_allo, 1'b0};
|
|
ROM_ADDR_NOP:
|
|
rom_addr <= last_rom_addr;
|
|
default:
|
|
rom_addr <= 0;
|
|
endcase
|
|
end
|
|
|
|
always @(*)
|
|
begin
|
|
// ---------------------------------------------
|
|
// -------- MAIN FINITE STATE MACHINE ----------
|
|
// ---------------------------------------------
|
|
|
|
// FSM defaults:
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
coeff_cnt_update <= COEFF_CNT_NOP;
|
|
|
|
next_state <= cur_state;
|
|
|
|
coeff_stb <= 0;
|
|
jmpmsb_load <= 0;
|
|
amp1_load <= 0;
|
|
amp2_load <= 0;
|
|
period_load <= 0;
|
|
dur_load <= 0;
|
|
allo_load <= 0;
|
|
load_cur_cmd <= 0;
|
|
set_ldq <= 0;
|
|
reset_ldq <= 0;
|
|
serve_pitch_data <= 0;
|
|
dur_cnt_clear <= 0;
|
|
|
|
case (cur_state)
|
|
S_IDLE:
|
|
begin
|
|
set_ldq <= 1;
|
|
if (serve_next_allo_data == 1)
|
|
begin
|
|
// we've run out of allophone data ..
|
|
end
|
|
if (data_stb == 1)
|
|
begin
|
|
allo_load <= 1;
|
|
next_state <= S_JMPADDR1;
|
|
end
|
|
rom_addr_sel <= ROM_ADDR_ZERO;
|
|
end
|
|
S_JMPADDR1:
|
|
begin
|
|
// get MSB of allophone address code
|
|
rom_addr_sel <= ROM_ADDR_ALLO;
|
|
next_state <= S_JMPADDR2;
|
|
end
|
|
S_JMPADDR2:
|
|
begin
|
|
// get LSB of allophone address code
|
|
jmpmsb_load <= 1;
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_CMDLOAD;
|
|
end
|
|
S_JMPADDR3:
|
|
begin
|
|
next_state <= S_CMDLOAD;
|
|
end
|
|
S_CMDLOAD:
|
|
begin
|
|
// perform jmp
|
|
rom_addr_sel <= ROM_ADDR_JMP;
|
|
next_state <= S_CMDDECODE;
|
|
reset_ldq <= 1;
|
|
end
|
|
S_CMDDECODE:
|
|
begin
|
|
// current command and
|
|
// terminate sequencer if
|
|
// CMD == 0xF
|
|
if (rom_data == 4'hF)
|
|
next_state <= S_IDLE; // end of command
|
|
else
|
|
begin
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_LOADAMP1;
|
|
end
|
|
load_cur_cmd <= 1;
|
|
//cur_cmd <= rom_data;
|
|
end
|
|
S_LOADAMP1: // load 16-bit AMP
|
|
begin
|
|
amp1_load <= 1;
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_LOADAMP2;
|
|
end
|
|
S_LOADAMP2:
|
|
begin
|
|
amp2_load <= 1;
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_LOADDUR;
|
|
end
|
|
S_LOADDUR:
|
|
begin
|
|
dur_load <= 1;
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_LOADPERIOD;
|
|
end
|
|
S_LOADPERIOD:
|
|
begin
|
|
period_load <= 1;
|
|
coeff_cnt_update <= COEFF_CNT_ZERO;
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_SERVEGATE;
|
|
end
|
|
S_SERVEGATE:
|
|
begin
|
|
rom_addr_sel <= ROM_ADDR_NOP;
|
|
if (serve_next_allo_data == 1)
|
|
begin
|
|
serve_pitch_data <= 1;
|
|
dur_cnt_clear <= 1;
|
|
next_state <= (cur_cmd == 4'd2) ? S_LOADCOEF1 : S_CMDDECODE;
|
|
end
|
|
end
|
|
S_LOADCOEF1:
|
|
// send F coefficient
|
|
begin
|
|
coeff_stb <= 1;
|
|
coeff_out <= coeff10bit;
|
|
coeff_cnt_update <= COEFF_CNT_INC;
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
next_state <= S_LOADCOEF2;
|
|
end
|
|
S_LOADCOEF2:
|
|
// send B coefficient
|
|
begin
|
|
coeff_stb <= 1;
|
|
coeff_out <= coeff10bit;
|
|
if (coeff_cnt == 3'd6)
|
|
next_state <= S_CMDDECODE; // load next section
|
|
else
|
|
next_state <= S_LOADCOEF1; // next command
|
|
|
|
rom_addr_sel <= ROM_ADDR_INC;
|
|
end
|
|
default:
|
|
next_state <= S_IDLE;
|
|
endcase
|
|
end
|
|
|
|
endmodule
|