Last active
December 2, 2025 16:09
-
-
Save sylefeb/2f25a84c76184970fddb10143f0bcea0 to your computer and use it in GitHub Desktop.
step3.si with sound generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SL 2022-01-10 @sylefeb | |
| // https://github.com/sylefeb/Silice | |
| // MIT license, see LICENSE_MIT in Silice repo root | |
| // Pre-compilation script, embeds compiled code within a string | |
| // Code has to be compiled into firmware/code.hex before | |
| $$dofile('pre_include_compiled.lua') | |
| // Setup memory size | |
| // - addrW is the address bus width | |
| // - the topmost bit is used to indicate peripheral access | |
| // - we thus allocate 2^(addrW-1) uint32 of RAM | |
| $$addrW = 15 | |
| $$periph_bit = addrW-1 | |
| // Configure BRAM (needed for write mask) | |
| $$config['bram_wmask_byte_wenable_width'] = 'data' | |
| // Includes the processor | |
| $include('../../../projects/ice-v/CPUs/ice-v.si') | |
| // Includes the SPIscreen driver | |
| $$ OLED_SLOW=1 | |
| $include('../../../projects/ice-v/SOCs/ice-v-oled.si') | |
| // Memory interface between SOC and CPU | |
| group mem_io | |
| { | |
| uint4 wenable(0), // write enable mask (xxxx, 0:nop, 1:write) | |
| uint32 wdata(0), // data to write | |
| uint32 rdata(0), // data read from memory | |
| uint$addrW$ addr(0), // address, init is boot address | |
| } | |
| // -------------------------------------------------- | |
| // Sound generator | |
| // -------------------------------------------------- | |
| unit sndgen(input uint24 inc_per_cycle,input uint8 amplitude, | |
| output uint8 audio) | |
| { | |
| uint25 cnt = 0; | |
| always { | |
| uint32 adjusted = (cnt[0,24] * amplitude) >> 8; | |
| audio = adjusted[16,8]; | |
| if (amplitude != 0) { | |
| // __display("P:%d A:%d",inc_per_cycle,amplitude); | |
| __display("cnt:%b",cnt); | |
| } | |
| if (cnt > (1<<24)) { | |
| cnt = 0; | |
| } else { | |
| cnt = cnt + inc_per_cycle; | |
| } | |
| } | |
| } | |
| // -------------------------------------------------- | |
| // SOC unit (main) | |
| // -------------------------------------------------- | |
| // some input/outputs do not exist in simulation and | |
| // are therefore enclosed in pre-processor conditions | |
| unit main( | |
| output uint8 leds, | |
| $$if BUTTONS then | |
| input uint7 btns, | |
| $$end | |
| $$if AUDIO then | |
| output uint4 audio_l, | |
| output uint4 audio_r, | |
| $$end | |
| output uint1 oled_clk, | |
| output uint1 oled_mosi, | |
| output uint1 oled_dc, | |
| output uint1 oled_resn, | |
| output uint1 oled_csn(0), | |
| $$if VERILATOR then | |
| // configuration for SPIscreen simulation | |
| output uint2 spiscreen_driver(1/*SSD1351*/), | |
| output uint10 spiscreen_width(128), | |
| output uint10 spiscreen_height(128), | |
| $$end | |
| $$if SDCARD then | |
| output uint1 sd_clk, | |
| output uint1 sd_csn, | |
| output uint1 sd_mosi, | |
| input uint1 sd_miso, | |
| $$end | |
| ) { | |
| $$if SIMULATION then | |
| // count cycles in simulation for debugging purposes | |
| uint32 cycle(0); | |
| $$end | |
| // SPIscreen (OLED) controller chip | |
| oled display( | |
| oled_din :> oled_mosi, | |
| oled_clk :> oled_clk, | |
| oled_dc :> oled_dc, | |
| ); | |
| $$if not SDCARD then | |
| // for simulation ('fake' inputs/outputs) | |
| uint1 sd_clk(0); | |
| uint1 sd_csn(0); | |
| uint1 sd_mosi(0); | |
| uint1 sd_miso(0); | |
| $$end | |
| $$if not AUDIO then | |
| // for simulation ('fake' inputs/outputs) | |
| uint4 audio_l(0); | |
| uint4 audio_r(0); | |
| $$end | |
| // Sound generator | |
| sndgen sg; | |
| // RAM | |
| // Instantiate the memory interface | |
| mem_io memio; | |
| // Instantiate a BRAM holding the system's RAM, 32bits words | |
| // -> uses template "bram_wmask_byte", that turns wenable into a byte mask | |
| bram uint32 ram<"bram_wmask_byte">[$1<<(addrW-1)$] = $meminit$; | |
| // Instantiate our CPU | |
| rv32i_cpu cpu( mem <:> memio ); | |
| // Variables to record previous cycle CPU access (peripherals memory mapping) | |
| // The CPU issues a memory request a cycle i and expects the result at i+1 | |
| uint$addrW$ prev_mem_addr(0); | |
| uint4 prev_mem_rw(0); | |
| uint32 prev_wdata(0); | |
| // --- SOC logic, the always block is always active | |
| always { | |
| display.enable = 0; // maintain display enable low (pulses on use) | |
| // ---- check whether the CPU read from or wrote to a peripheral address | |
| uint1 peripheral = prev_mem_addr[$periph_bit$,1]; | |
| uint1 peripheral_r = peripheral & (prev_mem_rw == 4b0); // reading periph. | |
| uint1 peripheral_w = peripheral & (prev_mem_rw != 4b0); // writing periph. | |
| uint1 audio_access = prev_mem_addr[$periph_bit-1$,1]; | |
| uint1 leds_access = prev_mem_addr[ 0,1]; | |
| uint1 display_direct_access = prev_mem_addr[ 1,1]; | |
| uint1 display_reset_access = prev_mem_addr[ 2,1]; | |
| uint1 sd_access = prev_mem_addr[ 5,1]; | |
| uint1 sndgen_access = prev_mem_addr[ 7,1]; | |
| // ---- memory access CPU <-> BRAM (reads and writes) | |
| // reads RAM, peripherals => CPU | |
| memio.rdata = // read data is either from memory or SOC peripherals | |
| // CPU reading from RAM | |
| (~peripheral_r ? ram.rdata : 32b0) | |
| // CPU reading from peripherals | |
| | ((peripheral_r & sd_access) ? {31b0, sd_miso} : 32b0) | |
| ; | |
| // writes CPU => RAM | |
| ram.wenable = memio.wenable & {4{~memio.addr[$periph_bit$,1]}}; | |
| // ^^^^^^^ no write if on peripheral addresses | |
| ram.wdata = memio.wdata; | |
| ram.addr = memio.addr; | |
| // TEST: sound generator to audio | |
| audio_l = sg.audio[4,4]; | |
| audio_r = sg.audio[4,4]; | |
| // writes CPU => peripherals | |
| if (peripheral_w) { | |
| /// LEDs | |
| leds = leds_access ? prev_wdata[0,8] : leds; | |
| /// sopund generator | |
| sg.inc_per_cycle = sndgen_access ? prev_wdata[8,24] : sg.inc_per_cycle; | |
| sg.amplitude = sndgen_access ? prev_wdata[0, 8] : sg.amplitude; | |
| /// display | |
| if (display_direct_access) { | |
| // -> whether to send command or data | |
| display.enable = (prev_wdata[9,1] | prev_wdata[10,1]); | |
| // -> byte to send | |
| display.byte = prev_wdata[0,8]; | |
| // -> data or command | |
| display.data_or_command = prev_wdata[10,1]; | |
| } | |
| // -> SPIscreen reset | |
| oled_resn = ~ (display_reset_access & prev_wdata[0,1]); | |
| /// sdcard output pins | |
| sd_clk = sd_access ? prev_wdata[0,1] : sd_clk; | |
| sd_mosi = sd_access ? prev_wdata[1,1] : sd_mosi; | |
| sd_csn = sd_access ? prev_wdata[2,1] : sd_csn; | |
| /// audio | |
| // if (audio_access) {audio_l = prev_wdata[0,8]; audio_r = prev_wdata[0,8];} | |
| $$if SIMULATION then | |
| // Add some simulation debug output here, convenient during development! | |
| if (leds_access) { | |
| __display("[cycle %d] LEDs: %b (%d)",cycle,leds,prev_wdata); | |
| if (leds == 255) { __finish(); }// special LED value stops simulation | |
| // convenient to interrupt from firmware | |
| } | |
| if (audio_access) { | |
| __display("[cycle %d] audio sample %x",cycle,audio_l); | |
| } | |
| $$end | |
| } | |
| // record current access for next cycle memory mapping checks | |
| prev_mem_addr = memio.addr; | |
| prev_mem_rw = memio.wenable; | |
| prev_wdata = memio.wdata; | |
| $$if SIMULATION then | |
| cycle = cycle + 1; | |
| $$end | |
| } // end of always block | |
| } | |
| // -------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment