Commit 2235a332 authored by Christian Fibich's avatar Christian Fibich Committed by Christian Fibich
Browse files

Initial

parent 7aa41220
.PHONY : all clean prog
all : prog
audio.blif: *.v uart2bus/*.v usynth/*.v
yosys -v4 -l synth.log -p 'synth_ice40 -top hx8k_audio_top -blif $@' $^
audio.asc: audio.blif audio.pcf
arachne-pnr -d 8k -o audio.asc -p audio.pcf audio.blif -P ct256
audio.bin: audio.asc
icepack audio.asc audio.bin
prog : audio.bin
iceprog audio.bin
clean :
rm audio.blif audio.asc
set_io clock_in J3
set_io tx B12
set_io rx B10
set_io pwm C16
module hx8k_audio_top #(parameter NUM_VOICES = 8, parameter WAVEFORM = 0) ( input clock_in,
input rx,
output tx,
output pwm);
reg [NUM_VOICES*16-1:0] freq;
reg [NUM_VOICES*16-1:0] a;
reg [NUM_VOICES*16-1:0] d;
reg [NUM_VOICES*16-1:0] s;
reg [NUM_VOICES*16-1:0] r;
wire locked;
wire reset;
reg [2:0] reset_sync;
wire clock;
reg [7:0] i;
reg [NUM_VOICES-1:0] play_note;
reg [NUM_VOICES-1:0] envelope;
reg [NUM_VOICES-1:0] enable;
reg [NUM_VOICES*8-1:0] freq_l;
reg [NUM_VOICES*8-1:0] a_l;
reg [NUM_VOICES*8-1:0] d_l;
reg [NUM_VOICES*8-1:0] s_l;
reg [NUM_VOICES*8-1:0] r_l;
pll clkgen (.clock_in(clock_in),.clock_out(clock),.locked(locked));
always @(posedge clock or negedge locked) begin
if (!locked) begin
reset_sync <= 3'b1;
end else begin
reset_sync <= {reset_sync[1:0],1'b0};
end
end
assign reset = reset_sync[2];
usynth #(.NUM_VOICES(NUM_VOICES), .WAVEFORM(WAVEFORM)) syn
(.clock(clock), .ce25(1), .reset(reset),
.play_note(play_note[NUM_VOICES-1:0]),
.enable(enable[NUM_VOICES-1:0]),
.freq(freq[NUM_VOICES*16-1:0]),
.pwm(pwm),
.a(a[NUM_VOICES*16-1:0]),
.d(d[NUM_VOICES*16-1:0]),
.s(s[NUM_VOICES*16-1:0]),
.r(r[NUM_VOICES*16-1:0]),
.envelope(envelope[NUM_VOICES-1:0]));
wire [15:0] address;
wire [7:0] data;
wire wr;
uart2bus_top i_uart (.clock(clock),.reset(reset),
.ser_in(rx),.ser_out(tx),
.int_address(address),
.int_wr_data(data),
.int_write(wr),
.int_rd_data(0),
.int_read(),
.int_req(),
.int_gnt(1));
always @(posedge(clock) or posedge(reset))
begin
if (reset) begin
for (i=0;i<NUM_VOICES;i=i+1) begin
freq_l[i*8+:8] <= 0;
a_l[i*8+:8] <= 0;
d_l[i*8+:8] <= 0;
s_l[i*8+:8] <= 0;
r_l[i*8+:8] <= 0;
freq [i*16+:16] <= 440;
a[i*16+:16] <= 16'h400;
d[i*16+:16] <= 16'h400;
s[i*16+:16] <= 16'h8000;
r[i*16+:16] <= 16'h0100;
enable <= 0;
play_note <= 0;
envelope <= 0;
end
end else begin
if (wr)
if (address > 16'hFF) begin
enable[address[3:0]] <= data[0];
play_note[address[3:0]] <= data[1];
envelope[address[3:0]] <= data[2];
end else begin
case (address[3:0])
4'h0: freq_l[address[7:4]*8 +:8] <= data;
4'h1: freq [address[7:4]*16+:16] <= {data, freq_l[address[7:4]*8+:8]};
4'h2: a_l [address[7:4]*8 +:8] <= data;
4'h3: a [address[7:4]*16+:16] <= {data, a_l[address[7:4]*8 +:8]};
4'h4: d_l [address[7:4]*8 +:8] <= data;
4'h5: d [address[7:4]*16+:16] <= {data, d_l[address[7:4]*8 +:8]};
4'h6: s_l [address[7:4]*8 +:8] <= data;
4'h7: s [address[7:4]*16+:16] <= {data, s_l[address[7:4]*8 +:8]};
4'h8: r_l [address[7:4]*8 +:8] <= data;
4'h9: r [address[7:4]*16+:16] <= {data, r_l[address[7:4]*8 +:8]};
default : play_note <= play_note;
endcase
end
end
end
endmodule
#!/usr/bin/env python2
import mido
import serial
import sys
import time
def get_first_voice(voices,note=None):
for i in range(0,len(voices)):
if voices[i] == note:
return i
return None
def set_freq(port,voice,freq):
write(port,0x10*(voice),[freq%256,freq/256])
def set_adsr(port,voice,a,d,s,r):
write(port,0x10*(voice)+2,[a%256,a/256,d%256,d/256,s%256,s/256,r%256,r/256])
def note_on(port,voice):
write(port,0x100+voice,[7])
def note_off(port,voice):
write(port,0x100+voice,[5])
def freq(note):
return (2.0**((note - 69)/12.0))*440.0;
def write(port,address,bytes):
cmd = [0, # Binary Mode
0x21] # Write Command + ACK
if address > 65535:
raise ValueError("Out of address space")
if len(bytes) > 256:
raise ValueError("Too many bytes")
data = list(cmd);
data.append(address / 256)
data.append(address % 256)
size = min(256,len(bytes))
data.append(size % 256)
payload = bytes
data.extend(payload)
port.write(data)
try:
rv = port.read(1) # ACK
if (len(rv) == 1):
rv = ord(rv[0])
else:
rv = 0
except serial.SerialTimeoutException:
rv = 0
if rv != 0x5a:
return False
return True
if __name__=="__main__":
if len(sys.argv) != 3:
print "Usage: %s <MIDI FILE> <PORT>" % sys.argv[0]
sys.exit(-1)
try:
port = serial.Serial(sys.argv[2],460800,timeout=1)
except serial.SerialException as e:
sys.stderr.write("Port %s cannot be opened.\n%s\n" % (sys.argv[2],str(e)))
sys.exit(-1)
voices = [None]*8
#write(port,0x100,[5]*8) # Voices on + envelope
for v in range(0,8):
note_off(port,v)
set_freq(port,v,440)
set_adsr(port,v,0x400,0x400,0x2000,0x100)
mid = mido.MidiFile(sys.argv[1])
for msg in mid.play():
if msg.type == "note_on":
if (msg.velocity > 0):
idx = get_first_voice(voices,None)
if idx is None:
print "WARN: no free voices"
else:
voices[idx] = msg.note
set_freq(port,idx,int(freq(msg.note)))
note_on(port,idx)
print "%d on (%d hz)" % (idx, int(freq(msg.note)))
else:
idx = get_first_voice(voices,msg.note)
if idx is None:
print "WARN: no note assigned"
else:
voices[idx] = None
print "%d off" % idx
note_off(port,idx)
#time.sleep(msg.time)
elif msg.type == "note_off":
idx = get_first_voice(voices,msg.note)
if idx is None:
print "WARN: no note assigned"
else:
voices[idx] = None
print "%d off" % idx
note_off(port,idx)
#time.sleep(msg.time)
/**
* PLL configuration
*
* This Verilog module was generated automatically
* using the icepll tool from the IceStorm project.
* Use at your own risk.
*
* Given input frequency: 12.000 MHz
* Requested output frequency: 24.000 MHz
* Achieved output frequency: 24.000 MHz
*/
module pll(
input clock_in,
output clock_out,
output locked
);
SB_PLL40_CORE #(
.FEEDBACK_PATH("SIMPLE"),
.DIVR(4'b0000), // DIVR = 0
.DIVF(7'b0111111), // DIVF = 63
.DIVQ(3'b101), // DIVQ = 5
.FILTER_RANGE(3'b001) // FILTER_RANGE = 1
) uut (
.LOCK(locked),
.RESETB(1'b1),
.BYPASS(1'b0),
.REFERENCECLK(clock_in),
.PLLOUTCORE(clock_out),
);
endmodule
#!/usr/bin/env python2
import serial
import sys
import time
NOTE_DURATION=0.5
PAUSE_DURATION=1
DURATION_FULL=96
DURATION_HALF=DURATION_FULL/2
DURATION_QUARTER=DURATION_HALF/2
DURATION_8TH=DURATION_QUARTER/2
DURATION_16TH=DURATION_8TH/2
DURATION_HALF_DOT=DURATION_HALF+DURATION_QUARTER
DURATION_QUARTER_DOT=DURATION_QUARTER+DURATION_8TH
## Frequencies
note={
'C0': 16.35,
'C#0': 17.32,
'D0': 18.35,
'D#0': 19.45,
'E0': 20.60,
'F0': 21.83,
'F#0': 23.12,
'G0': 24.50,
'G#0': 25.96,
'A0': 27.50,
'A#0': 29.14,
'H0': 30.87,
'C1': 32.70,
'C#1': 34.65,
'D1': 36.71,
'D#1': 38.89,
'E1': 41.20,
'F1': 43.65,
'F#1': 46.25,
'G1': 49.00,
'G#1': 51.91,
'A1': 55.00,
'A#1': 58.27,
'H1': 61.74,
'C2': 65.41,
'C#2': 69.30,
'D2': 73.42,
'D#2': 77.78,
'E2': 82.41,
'F2': 87.31,
'F#2': 92.50,
'G2': 98.00,
'G#2': 103.83,
'A2': 110.00,
'A#2': 116.54,
'H2': 123.47,
'C3': 130.81,
'C#3': 138.59,
'D3': 146.83,
'D#3': 155.56,
'E3': 164.81,
'F3': 174.61,
'F#3': 185.00,
'G3': 196.00,
'G#3': 207.65,
'A3': 220.00,
'A#3': 233.08,
'H3': 246.94,
'C4': 261.63,
'C#4': 277.18,
'D4': 293.66,
'D#4': 311.13,
'E4': 329.63,
'F4': 349.23,
'F#4': 369.99,
'G4': 392.00,
'G#4': 415.30,
'A4': 440.00,
'A#4': 466.16,
'H4': 493.88,
'C5': 523.25,
'C#5': 554.37,
'D5': 587.33,
'D#5': 622.25,
'E5': 659.26,
'F5': 698.46,
'F#5': 739.99,
'G5': 783.99,
'G#5': 830.61,
'A5': 880.00,
'A#5': 932.33,
'H5': 987.77,
'C6': 1046.50,
'C#6': 1108.73,
'D6': 1174.66,
'D#6': 1244.51,
'E6': 1318.51,
'F6': 1396.91,
'F#6': 1479.98,
'G6': 1567.98,
'G#6': 1661.22,
'A6': 1760.00,
'A#6': 1864.66,
'H6': 1975.53,
'C7': 2093.00,
'C#7': 2217.46,
'D7': 2349.32,
'D#7': 2489.02,
'E7': 2637.02,
'F7': 2793.83,
'F#7': 2959.96,
'G7': 3135.96,
'G#7': 3322.44,
'A7': 3520.00,
'A#7': 3729.31,
'H7': 3951.07,
'C8': 4186.01,
'C#8': 4434.92,
'D8': 4698.64,
'D#8': 4978.03,
'E8': 5274.04,
'F8': 5587.65,
'F#8': 5919.91,
'G8': 6271.93,
'G#8': 6644.88,
'A8': 7040.00,
'A#8': 7458.62,
'H8': 7902.13,
'PAUSE':None};
#notes = [
# (note['PAUSE'], DURATION_8TH),
# (note['C4'], DURATION_8TH),
# (note['G4'], DURATION_8TH),
# (note['C4'], DURATION_8TH),
# (note['H4'], DURATION_8TH),
# (note['F4'], DURATION_8TH),
# (note['C4'], DURATION_8TH),
# (note['G4'], DURATION_8TH),
# (note['C4'], DURATION_QUARTER),
# (note['H4'], DURATION_HALF_DOT),
# (note['PAUSE'], DURATION_8TH),
# (note['C4'], DURATION_8TH),
# (note['G4'], DURATION_8TH),
# (note['C4'], DURATION_8TH),
# (note['H4'], DURATION_8TH),
# (note['F4'], DURATION_8TH),
# (note['C4'], DURATION_8TH),
# (note['G4'], DURATION_8TH),
# (note['C4'], DURATION_QUARTER),
# (note['H4'], DURATION_HALF_DOT)]
notes1 = [
# 1
(note['PAUSE'], DURATION_FULL),
# 2
(note['PAUSE'], DURATION_QUARTER),
(note['H2'], DURATION_8TH),
(note['H2'], DURATION_8TH),
(note['D3'], DURATION_8TH),
(note['D3'], DURATION_8TH),
(note['F3'], DURATION_8TH),
(note['F3'], DURATION_8TH),
# 3
(note['E3'], DURATION_FULL),
# 4
(note['PAUSE'], DURATION_8TH),
(note['F#2'], DURATION_8TH),
(note['F#2'], DURATION_8TH),
(note['F#2'], DURATION_8TH),
(note['C3'], DURATION_8TH),
(note['C3'], DURATION_8TH),
(note['E3'], DURATION_8TH),
(note['E3'], DURATION_8TH),
# 5
(note['D3'], DURATION_FULL),
# 6
(note['PAUSE'], DURATION_8TH),
(note['D3'], DURATION_8TH),
(note['D3'], DURATION_8TH),
(note['D3'], DURATION_8TH),
(note['F3'], DURATION_8TH),
(note['F3'], DURATION_8TH),
(note['H3'], DURATION_8TH),
(note['H3'], DURATION_8TH),
# 7
(note['C4'], DURATION_HALF),
(note['H3'], DURATION_HALF),
# 8
(note['PAUSE'], DURATION_QUARTER),
(note['D3'], DURATION_8TH),
(note['D3'], DURATION_8TH),
(note['F3'], DURATION_8TH),
(note['F3'], DURATION_8TH),
(note['H3'], DURATION_8TH),
(note['H3'], DURATION_8TH),
# 9
(note['C4'], DURATION_HALF),
(note['H3'], DURATION_HALF),
# 10
(note['PAUSE'], DURATION_QUARTER),
(note['D3'], DURATION_8TH),
(note['D3'], DURATION_8TH),
# 11
(note['C4'], DURATION_8TH),
(note['C4'], DURATION_QUARTER_DOT),
(note['C4'], DURATION_QUARTER),
(note['C4'], DURATION_8TH),
(note['D4'], DURATION_8TH),
# 12
(note['E4'], DURATION_8TH),
(note['E4'], DURATION_QUARTER_DOT),
(note['D4'], DURATION_8TH),
(note['C4'], DURATION_QUARTER_DOT),
# 13
(note['D4'], DURATION_HALF_DOT),
(note['C4'], DURATION_8TH),
(note['D4'], DURATION_8TH)
];
notes2 = [
#1
(note['D4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['E4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['D4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['E4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
# 2
(note['D4'], DURATION_HALF),
(note['A4'], DURATION_QUARTER),
(note['F4'], DURATION_QUARTER),
# 3
(note['C4'], DURATION_8TH),
(note['G4'], DURATION_8TH),
(note['D4'], DURATION_8TH),
(note['G4'], DURATION_8TH),
(note['C4'], DURATION_8TH),
(note['G4'], DURATION_8TH),
(note['E4'], DURATION_8TH),
(note['G4'], DURATION_8TH),
# 4
(note['C4'], DURATION_HALF),
(note['G4'], DURATION_QUARTER),
(note['E4'], DURATION_QUARTER),
# 5
(note['D4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['E4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['D4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['E4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
# 6
(note['D4'], DURATION_HALF),
(note['F4'], DURATION_QUARTER),
(note['A4'], DURATION_QUARTER),
# 7
(note['F4'], DURATION_8TH),
(note['D5'], DURATION_8TH),
(note['A#4'], DURATION_8TH), # HB4
(note['D5'], DURATION_8TH),
(note['F4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['G4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
# 8
(note['A4'], DURATION_HALF),
(note['F4'], DURATION_QUARTER),
(note['A4'], DURATION_QUARTER),
# 9
(note['F4'], DURATION_8TH),
(note['D5'], DURATION_8TH),
(note['A#4'], DURATION_8TH), # HB4
(note['D5'], DURATION_8TH),
(note['F4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['G4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
# 10
(note['A4'], DURATION_HALF),
# 11
(note['A#4'], DURATION_8TH),
(note['F5'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['F5'], DURATION_8TH),
(note['A#4'], DURATION_8TH),
(note['F5'], DURATION_8TH),
(note['A#4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
# 12
(note['D5'], DURATION_8TH),
(note['F5'], DURATION_8TH),
(note['A#4'], DURATION_8TH),
(note['F5'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['A#4'], DURATION_8TH),
(note['F5'], DURATION_8TH),
(note['A#4'], DURATION_8TH),
# 13
(note['F4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['G4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['A4'], DURATION_8TH),
(note['C5'], DURATION_8TH),
(note['A#4'], DURATION_8TH),
(note['A4'], DURATION_8TH),
];
def set_freq(port,voice,freq):
write(port,0x10*(voice+1),[freq%256,freq/256])
def set_adsr(port,voice,a,d,s,r):
write(port,0x10*(voice+1)+2,[a%256,a/256,d%256,d/256,s%256,s/256,r%256,r/256])
def note_on(port,voice):
write(port,voice,[7])