├── tools ├── .gitignore └── src │ ├── wigen │ ├── CMakeLists.txt │ ├── range.h │ ├── range.cpp │ ├── generator.h │ └── main.cpp │ ├── lxp32dump │ ├── CMakeLists.txt │ ├── disassembler.h │ └── main.cpp │ ├── lxp32asm │ ├── CMakeLists.txt │ ├── linker.h │ ├── outputwriter.h │ ├── utils.h │ ├── linkableobject.h │ ├── outputwriter.cpp │ ├── utils.cpp │ └── assembler.h │ └── CMakeLists.txt ├── doc ├── lxp32-logo.png ├── lxp32-trm.pdf └── src │ ├── trm │ ├── .gitignore │ ├── images │ │ ├── gtkwave.png │ │ ├── symbols.pdf │ │ ├── dbustiming.pdf │ │ ├── ibustiming.pdf │ │ ├── llitiming.pdf │ │ ├── lxp32-logo.pdf │ │ ├── resetsync.pdf │ │ ├── blockdiagram.pdf │ │ └── instructionformat.pdf │ ├── preamble.tex │ └── frontmatter.tex │ └── logo │ └── lxp32-logo.svg ├── verify ├── icache │ ├── run │ │ ├── ghdl │ │ │ ├── .gitignore │ │ │ └── Makefile │ │ ├── vsim │ │ │ ├── .gitignore │ │ │ └── Makefile │ │ └── xsim │ │ │ ├── .gitignore │ │ │ └── Makefile │ └── src │ │ ├── make │ │ └── sources.make │ │ └── tb │ │ ├── tb_pkg.vhd │ │ ├── ram_model.vhd │ │ ├── tb.vhd │ │ └── cpu_model.vhd ├── lxp32 │ ├── run │ │ ├── vsim │ │ │ ├── .gitignore │ │ │ └── Makefile │ │ ├── ghdl │ │ │ ├── .gitignore │ │ │ └── Makefile │ │ └── xsim │ │ │ ├── .gitignore │ │ │ └── Makefile │ └── src │ │ ├── firmware │ │ ├── test017.asm │ │ ├── test016.asm │ │ ├── test014.asm │ │ ├── test006.asm │ │ ├── test003.asm │ │ ├── test022.asm │ │ ├── test019.asm │ │ ├── test020.asm │ │ ├── test008.asm │ │ ├── test010.asm │ │ ├── test015.asm │ │ ├── test005.asm │ │ ├── test002.asm │ │ ├── test009.asm │ │ ├── test021.asm │ │ └── test007.asm │ │ ├── platform │ │ ├── scrambler.vhd │ │ ├── coprocessor.vhd │ │ ├── ibus_adapter.vhd │ │ ├── dbus_monitor.vhd │ │ ├── timer.vhd │ │ ├── program_ram.vhd │ │ ├── sync_fifo.vhd │ │ ├── generic_dpram.vhd │ │ └── intercon.vhd │ │ ├── tb │ │ ├── tb_pkg.vhd │ │ ├── monitor.vhd │ │ ├── tb_pkg_body.vhd │ │ └── tb.vhd │ │ └── make │ │ └── sources.make └── common_pkg │ ├── common_pkg.vhd │ └── common_pkg_body.vhd ├── misc └── highlight │ ├── akelpad │ └── asm.coder │ ├── readme.txt │ └── notepad++ │ └── LXP32Assembly.xml ├── .gitattributes ├── rtl ├── lxp32_mul16x16.vhd ├── lxp32_compl.vhd ├── lxp32_mul_seq.vhd ├── lxp32_ram256x32.vhd ├── lxp32_ubuf.vhd ├── lxp32_mul_dsp.vhd ├── lxp32_scratchpad.vhd ├── lxp32u_top.vhd ├── lxp32_shifter.vhd ├── lxp32c_top.vhd ├── lxp32_interrupt_mux.vhd ├── lxp32_dbus.vhd ├── lxp32_divider.vhd ├── lxp32_mul_opt.vhd ├── lxp32_alu.vhd ├── lxp32_execute.vhd ├── lxp32_fetch.vhd └── lxp32_cpu.vhd ├── LICENSE.md └── README.md /tools/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /build 3 | -------------------------------------------------------------------------------- /doc/lxp32-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/lxp32-logo.png -------------------------------------------------------------------------------- /doc/lxp32-trm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/lxp32-trm.pdf -------------------------------------------------------------------------------- /doc/src/trm/.gitignore: -------------------------------------------------------------------------------- 1 | /*.aux 2 | /*.log 3 | /*.gz 4 | /*.toc 5 | /*.pdf 6 | /*.out 7 | -------------------------------------------------------------------------------- /verify/icache/run/ghdl/.gitignore: -------------------------------------------------------------------------------- 1 | *.vcd 2 | *.cf 3 | *.o 4 | tb 5 | compile.stamp 6 | -------------------------------------------------------------------------------- /verify/icache/run/vsim/.gitignore: -------------------------------------------------------------------------------- 1 | /work 2 | *.stamp 3 | *.wlf 4 | *.ini 5 | *.o 6 | transcript 7 | -------------------------------------------------------------------------------- /verify/lxp32/run/vsim/.gitignore: -------------------------------------------------------------------------------- 1 | /work 2 | *.ram 3 | *.stamp 4 | *.wlf 5 | *.ini 6 | *.o 7 | transcript 8 | -------------------------------------------------------------------------------- /doc/src/trm/images/gtkwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/gtkwave.png -------------------------------------------------------------------------------- /doc/src/trm/images/symbols.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/symbols.pdf -------------------------------------------------------------------------------- /doc/src/trm/images/dbustiming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/dbustiming.pdf -------------------------------------------------------------------------------- /doc/src/trm/images/ibustiming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/ibustiming.pdf -------------------------------------------------------------------------------- /doc/src/trm/images/llitiming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/llitiming.pdf -------------------------------------------------------------------------------- /doc/src/trm/images/lxp32-logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/lxp32-logo.pdf -------------------------------------------------------------------------------- /doc/src/trm/images/resetsync.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/resetsync.pdf -------------------------------------------------------------------------------- /misc/highlight/akelpad/asm.coder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/misc/highlight/akelpad/asm.coder -------------------------------------------------------------------------------- /doc/src/trm/images/blockdiagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/blockdiagram.pdf -------------------------------------------------------------------------------- /verify/lxp32/run/ghdl/.gitignore: -------------------------------------------------------------------------------- 1 | *.ram 2 | *.vcd 3 | *.fst 4 | *.fst.hier 5 | *.cf 6 | *.o 7 | tb 8 | compile.stamp 9 | -------------------------------------------------------------------------------- /doc/src/trm/images/instructionformat.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxp32/lxp32-cpu/HEAD/doc/src/trm/images/instructionformat.pdf -------------------------------------------------------------------------------- /verify/icache/run/xsim/.gitignore: -------------------------------------------------------------------------------- 1 | /.Xil 2 | /xsim.dir 3 | webtalk* 4 | xelab* 5 | xsim* 6 | xvhdl* 7 | hs_err* 8 | vivado* 9 | *.stamp 10 | *.wdb 11 | -------------------------------------------------------------------------------- /verify/lxp32/run/xsim/.gitignore: -------------------------------------------------------------------------------- 1 | /.Xil 2 | /xsim.dir 3 | webtalk* 4 | xelab* 5 | xsim* 6 | xvhdl* 7 | hs_err* 8 | vivado* 9 | *.ram 10 | *.stamp 11 | *.wdb 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Convert line endings for text files on Windows 2 | * text=auto 3 | 4 | # Prevent the GitHub parser from ignoring the "tools" directory contents 5 | /tools/* linguist-vendored=false 6 | -------------------------------------------------------------------------------- /tools/src/wigen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3.0) 2 | 3 | add_executable(wigen generator.cpp main.cpp range.cpp) 4 | 5 | # Install 6 | 7 | install(TARGETS wigen DESTINATION .) 8 | -------------------------------------------------------------------------------- /tools/src/lxp32dump/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3.0) 2 | 3 | add_executable(lxp32dump disassembler.cpp main.cpp) 4 | 5 | # Install 6 | 7 | install(TARGETS lxp32dump DESTINATION .) 8 | -------------------------------------------------------------------------------- /misc/highlight/readme.txt: -------------------------------------------------------------------------------- 1 | This directory contains LXP32 assembly language syntax highlighting 2 | rules for the following text editors: 3 | 4 | * AkelPad (http://akelpad.sourceforge.net/) 5 | * Notepad++ (https://notepad-plus-plus.org/) 6 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3.0) 2 | 3 | add_executable(lxp32asm assembler.cpp linkableobject.cpp linker.cpp main.cpp outputwriter.cpp utils.cpp) 4 | 5 | if(MSVC) 6 | # Make the program expand wildcard command-line arguments 7 | set_target_properties(lxp32asm PROPERTIES LINK_FLAGS "setargv.obj") 8 | endif() 9 | 10 | # Install 11 | 12 | install(TARGETS lxp32asm DESTINATION .) 13 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test017.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * Test the new "lcs" instruction 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lcs r101, halt 7 | lcs r102, failure 8 | 9 | lc r0, 1000000 10 | lc r1, -1000011 11 | lcs r10, 1000000 12 | lcs r11, -1000011 13 | 14 | cjmpne r102, r0, r10 // failure 15 | cjmpne r102, r1, r11 // failure 16 | 17 | sw r100, 1 18 | jmp r101 // halt 19 | 20 | failure: 21 | sw r100, 2 22 | 23 | halt: 24 | hlt 25 | jmp r101 // halt 26 | -------------------------------------------------------------------------------- /verify/icache/src/make/sources.make: -------------------------------------------------------------------------------- 1 | # CPU RTL 2 | 3 | LXP32_DIR=../../../../rtl 4 | LXP32_RTL=$(LXP32_DIR)/lxp32_ram256x32.vhd\ 5 | $(LXP32_DIR)/lxp32_icache.vhd 6 | 7 | # Common package 8 | 9 | COMMON_PKG_DIR=../../../common_pkg 10 | COMMON_SRC=$(COMMON_PKG_DIR)/common_pkg.vhd $(COMMON_PKG_DIR)/common_pkg_body.vhd 11 | 12 | # Testbench sources 13 | 14 | TB_DIR=../../src/tb 15 | TB_SRC=$(TB_DIR)/tb_pkg.vhd\ 16 | $(TB_DIR)/cpu_model.vhd\ 17 | $(TB_DIR)/ram_model.vhd\ 18 | $(TB_DIR)/tb.vhd 19 | 20 | TB_MOD=tb 21 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test016.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * Test wake-up interrupts 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, 0x30000000 // coprocessor input register 8 | lc r103, 0x30000004 // coprocessor output register 9 | lc r104, failure 10 | 11 | lcs cr, 0x0404 // enable coprocessor interrupt and mark it as wake-up 12 | 13 | sw r102, 33 14 | 15 | hlt 16 | 17 | lw r1, r103 18 | cjmpne r104, r1, 99 19 | 20 | sw r100, 1 // success 21 | 22 | halt: 23 | hlt 24 | jmp r101 // halt 25 | 26 | failure: 27 | sw r100, 2 28 | jmp r101 // halt 29 | -------------------------------------------------------------------------------- /verify/icache/src/tb/tb_pkg.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32 instruction cache testbench package 3 | -- 4 | -- Part of the LXP32 instruction cache testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Auxiliary package declaration for the LXP32 instruction cache 9 | -- testbench. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | 15 | package tb_pkg is 16 | constant xor_constant: std_logic_vector(31 downto 0):=X"12345678"; 17 | end package; 18 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test014.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * Test "hlt" instruction 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) 8 | lc r104, 0x20000004 // timer: delay between pulses (in cycles) 9 | 10 | lc iv0, timer_handler 11 | mov r10, 2 12 | mov cr, 1 // enable interrupt 0 13 | lc r0, 1000 14 | sw r104, r0 15 | sw r103, 1 16 | 17 | hlt 18 | 19 | sw r100, r10 // r10 will be 2 if interrupt hasn't been called, which is a failure code 20 | 21 | halt: 22 | hlt 23 | jmp r101 // halt 24 | 25 | timer_handler: 26 | mov r10, 1 27 | iret 28 | -------------------------------------------------------------------------------- /tools/src/wigen/range.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the Range class which represents 7 | * VHDL array ranges. 8 | */ 9 | 10 | #ifndef RANGE_H_INCLUDED 11 | #define RANGE_H_INCLUDED 12 | 13 | #include 14 | 15 | class Range { 16 | int _high; 17 | int _low; 18 | bool _valid; 19 | public: 20 | Range(); 21 | Range(int h,int l); 22 | 23 | void assign(int h,int l); 24 | void clear(); 25 | 26 | bool valid() const; 27 | int high() const; 28 | int low() const; 29 | int length() const; 30 | std::string toString() const; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test006.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test checks for a bug with jump destination register 3 | * being wrongly overwritten when jump instruction follows "lw" 4 | */ 5 | 6 | lc r100, 0x10000000 // test result output pointer 7 | lc r101, halt 8 | lc r102, failure 9 | 10 | lc r16, 0x10000004 11 | lc r17, 0x12345678 12 | lc r18, 0x12345678 13 | 14 | sw r16, 123 15 | lw r0, r16 16 | cjmpne r17, 0, 0 // r17 used to be wrongly overwritten by the value of r16 here 17 | 18 | sw r16, r17 19 | 20 | nop 21 | nop 22 | 23 | cjmpne r102, r17, r18 24 | 25 | sw r100, 1 26 | jmp r101 27 | 28 | failure: 29 | sw r100, 2 30 | 31 | halt: 32 | hlt 33 | jmp r101 34 | -------------------------------------------------------------------------------- /verify/icache/run/vsim/Makefile: -------------------------------------------------------------------------------- 1 | include ../../src/make/sources.make 2 | 3 | VCOMFLAGS=-93 4 | VSIMFLAGS=-t 1ps 5 | 6 | ######################## 7 | # Phony targets 8 | ######################## 9 | 10 | all: batch 11 | 12 | .PHONY: all compile batch gui clean 13 | 14 | compile: compile.stamp 15 | 16 | batch: compile.stamp 17 | vsim $(VSIMFLAGS) -do "run -all; quit -f" -c work.$(TB_MOD) 18 | 19 | gui: compile.stamp 20 | vsim $(VSIMFLAGS) work.$(TB_MOD) 21 | 22 | clean: 23 | rm -rf work 24 | rm -f modelsim.ini 25 | rm -f transcript 26 | rm -f vsim.wlf 27 | rm -f compile.stamp 28 | 29 | ######################## 30 | # Normal targets 31 | ######################## 32 | 33 | compile.stamp: $(LXP32_RTL) $(COMMON_SRC) $(TB_SRC) | work 34 | vcom $(VCOMFLAGS) $(LXP32_RTL) $(COMMON_SRC) $(TB_SRC) 35 | echo > compile.stamp 36 | 37 | work: 38 | vlib work 39 | vmap work work 40 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test003.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies that basic logical operations 3 | * (and, xor, or, not) work. 4 | */ 5 | 6 | lc r100, 0x10000000 // test result output pointer 7 | lc r101, halt 8 | lc r102, failure 9 | 10 | lc r0, 0xD54B65C0 11 | lc r1, 0xCE8870A8 12 | lc r16, 0x10000004 // destination pointer 13 | 14 | and r2, r0, r1 15 | sw r16, r2 16 | lc r3, 0xC4086080 17 | cjmpne r102, r2, r3 18 | 19 | or r2, r0, r1 20 | sw r16, r2 21 | lc r3, 0xDFCB75E8 22 | cjmpne r102, r2, r3 23 | 24 | xor r2, r0, r1 25 | sw r16, r2 26 | lc r3, 0x1BC31568 27 | cjmpne r102, r2, r3 28 | 29 | // Note: "not dst, src" is just an alias for "xor dst, src, -1" 30 | not r2, r0 31 | sw r16, r2 32 | lc r3, 0x2AB49A3F 33 | cjmpne r102, r2, r3 34 | 35 | sw r100, 1 36 | jmp r101 37 | 38 | failure: 39 | sw r100, 2 40 | 41 | halt: 42 | hlt 43 | jmp r101 44 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test022.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies the xcall instruction, which, unlike call, 3 | * stores the address of the next instruction to an arbitrary 4 | * register, not necessary rp. 5 | */ 6 | 7 | lc r100, 0x10000000 // test result output pointer 8 | lc r101, halt 9 | lc r102, failure 10 | lc r103, 0 11 | lc r104, next_instr_1 12 | lc r105, next_instr_2 13 | 14 | lcs r0, func_r200 15 | xcall r200, r0 16 | 17 | next_instr_1: 18 | lcs r0, func_r201 19 | xcall r201, r0 20 | 21 | next_instr_2: 22 | cjmpne r102, r103, 3 // failure 23 | cjmpne r102, r200, r104 // failure 24 | cjmpne r102, r201, r105 // failure 25 | 26 | sw r100, 1 27 | jmp r101 // halt 28 | 29 | failure: 30 | sw r100, 2 31 | 32 | halt: 33 | hlt 34 | jmp r101 // halt 35 | 36 | func_r200: 37 | add r103, r103, 1 38 | jmp r200 39 | 40 | func_r201: 41 | add r103, r103, 2 42 | jmp r201 43 | -------------------------------------------------------------------------------- /verify/common_pkg/common_pkg.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Common package for LXP32 testbenches 3 | -- 4 | -- Part of the LXP32 verification environment 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | --------------------------------------------------------------------- 8 | 9 | library ieee; 10 | use ieee.std_logic_1164.all; 11 | use ieee.numeric_std.all; 12 | 13 | package common_pkg is 14 | type rng_state_type is record 15 | seed1: positive; 16 | seed2: positive; 17 | end record; 18 | 19 | -- Generate a pseudo-random value of integer type from [a;b] range 20 | -- Output is stored in x 21 | procedure rand(variable st: inout rng_state_type; a,b: integer; variable x: out integer); 22 | 23 | -- Convert std_logic_vector to a hexadecimal string (similar to 24 | -- the "to_hstring" function from VHDL-2008 25 | function hex_string(x: std_logic_vector) return string; 26 | end package; 27 | -------------------------------------------------------------------------------- /verify/icache/run/xsim/Makefile: -------------------------------------------------------------------------------- 1 | include ../../src/make/sources.make 2 | 3 | ifeq ($(findstring Windows,$(OS)),) 4 | BAT= 5 | else 6 | BAT=.bat 7 | endif 8 | 9 | ######################## 10 | # Phony targets 11 | ######################## 12 | 13 | all: batch 14 | 15 | .PHONY: all compile batch gui clean 16 | 17 | compile: compile.stamp 18 | 19 | batch: compile.stamp 20 | xsim$(BAT) -R tb_sim 21 | 22 | gui: compile.stamp 23 | xsim$(BAT) -g -onfinish stop -onerror stop tb_sim 24 | 25 | clean: 26 | rm -rf .Xil 27 | rm -rf xsim.dir 28 | rm -f webtalk* 29 | rm -f xelab* 30 | rm -f xsim* 31 | rm -f xvhdl* 32 | rm -f hs_err* 33 | rm -f vivado* 34 | rm -f *.wdb 35 | rm -f compile.stamp 36 | 37 | ######################## 38 | # Normal targets 39 | ######################## 40 | 41 | compile.stamp: $(LXP32_RTL) $(COMMON_SRC) $(TB_SRC) 42 | xvhdl$(BAT) $(LXP32_RTL) $(COMMON_SRC) $(TB_SRC) 43 | xelab$(BAT) work.tb -s tb_sim -debug typical 44 | echo > compile.stamp 45 | -------------------------------------------------------------------------------- /verify/lxp32/run/vsim/Makefile: -------------------------------------------------------------------------------- 1 | include ../../src/make/sources.make 2 | 3 | VCOMFLAGS=-93 4 | VSIMFLAGS=-t 1ps 5 | 6 | ######################## 7 | # Phony targets 8 | ######################## 9 | 10 | all: batch 11 | 12 | .PHONY: all compile batch gui clean 13 | 14 | compile: compile.stamp 15 | 16 | batch: compile.stamp 17 | vsim $(VSIMFLAGS) -do "run -all; quit -f" -c work.$(TB_MOD) 18 | 19 | gui: compile.stamp 20 | vsim $(VSIMFLAGS) work.$(TB_MOD) 21 | 22 | clean: 23 | rm -rf work 24 | rm -f modelsim.ini 25 | rm -f transcript 26 | rm -f vsim.wlf 27 | rm -f $(FIRMWARE) 28 | rm -f compile.stamp 29 | 30 | ######################## 31 | # Normal targets 32 | ######################## 33 | 34 | compile.stamp: $(LXP32_RTL) $(COMMON_SRC) $(PLATFORM_RTL) $(TB_SRC) $(FIRMWARE) | work 35 | vcom $(VCOMFLAGS) $(LXP32_RTL) $(COMMON_SRC) $(PLATFORM_RTL) $(TB_SRC) 36 | echo > compile.stamp 37 | 38 | work: 39 | vlib work 40 | vmap work work 41 | 42 | %.ram: $(FW_SRC_DIR)/%.asm 43 | $(ASM) -f textio $^ -o $@ 44 | -------------------------------------------------------------------------------- /rtl/lxp32_mul16x16.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- A basic parallel 16x16 multiplier with an output register 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- A straightforward behavioral description. Can be replaced 9 | -- with a library component wrapper if needed. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; 15 | 16 | entity lxp32_mul16x16 is 17 | port( 18 | clk_i: in std_logic; 19 | a_i: in std_logic_vector(15 downto 0); 20 | b_i: in std_logic_vector(15 downto 0); 21 | p_o: out std_logic_vector(31 downto 0) 22 | ); 23 | end entity; 24 | 25 | architecture rtl of lxp32_mul16x16 is 26 | 27 | begin 28 | 29 | process (clk_i) is 30 | begin 31 | if rising_edge(clk_i) then 32 | p_o<=std_logic_vector(unsigned(a_i)*unsigned(b_i)); 33 | end if; 34 | end process; 35 | 36 | end architecture; 37 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test019.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies non-returnable interrupt handling 3 | * Note: "iret" is never called here 4 | */ 5 | 6 | lc r100, 0x10000000 // test result output pointer 7 | lcs r101, halt 8 | lc r102, failure 9 | lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) 10 | lc r104, 0x20000004 // timer: delay between pulses (in cycles) 11 | lcs r105, success 12 | 13 | lcs r32, 0 // counter 14 | 15 | lcs iv0, test_loop@1 // set IRF to mark the interrupt as non-returnable 16 | mov cr, 1 // enable timer interrupt 17 | sw r104, 100 // delay between interrupts 18 | sw r103, 100 // generate 100 interrupts 19 | hlt // wait for a non-returnable interrupt 20 | 21 | test_loop: 22 | add r32, r32, 1 23 | cjmpuge r105, r32, 100 // success 24 | hlt // wait for a non-returnable interrupt 25 | 26 | failure: 27 | sw r100, 2 // should never reach here 28 | jmp r101 // halt 29 | 30 | success: 31 | sw r100, 1 // success 32 | 33 | halt: 34 | hlt 35 | jmp r101 // halt 36 | -------------------------------------------------------------------------------- /verify/icache/run/ghdl/Makefile: -------------------------------------------------------------------------------- 1 | include ../../src/make/sources.make 2 | 3 | GHDL_FLAGS=--std=93 4 | 5 | WAVE_VCD=wave.vcd 6 | WAVE_OUT=wave.fst 7 | 8 | ######################## 9 | # Phony targets 10 | ######################## 11 | 12 | all: batch 13 | 14 | .PHONY: all compile batch gui clean 15 | 16 | .PRECIOUS: $(WAVE_OUT) 17 | 18 | compile: compile.stamp 19 | 20 | batch: compile.stamp 21 | ghdl -r $(GHDL_FLAGS) $(TB_MOD) 22 | 23 | gui: $(WAVE_OUT) 24 | gtkwave $(WAVE_OUT) 25 | 26 | clean: 27 | rm -f *.cf 28 | rm -f $(WAVE_VCD) 29 | rm -f $(WAVE_OUT) 30 | rm -f *.o 31 | rm -f $(TB_MOD) 32 | rm -f compile.stamp 33 | 34 | ######################## 35 | # Normal targets 36 | ######################## 37 | 38 | $(WAVE_OUT): $(WAVE_VCD) 39 | vcd2fst $^ $@ 40 | 41 | $(WAVE_VCD): compile.stamp 42 | ghdl -r $(GHDL_FLAGS) $(TB_MOD) --vcd=$(WAVE_VCD) 43 | 44 | compile.stamp: $(LXP32_RTL) $(COMMON_SRC) $(TB_SRC) 45 | ghdl -a $(GHDL_FLAGS) $(LXP32_RTL) $(COMMON_SRC) $(TB_SRC) 46 | ghdl -e $(GHDL_FLAGS) $(TB_MOD) 47 | echo > compile.stamp 48 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test020.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * Check that there are no pipeline hazards 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lcs r101, halt 7 | lcs r102, failure 8 | lcs r103, success 9 | 10 | add r0, 100, 50 // r0:=150 11 | add r1, r0, 3 // r1:=153, potential RAW hazard 12 | mul r2, r1, 109 // r2:=16677, potential RAW hazard 13 | mul r3, r2, r0 // r3:=2501550, potential RAW hazard 14 | sub r4, r3, 15 // r4:=2501535, potential RAW hazard 15 | 16 | mul r5, 50, 117 // r2:=5850 17 | sub r5, 100, 9 // r2:=91, overwrites previous result, potential WAW hazard 18 | 19 | lc r6, 1800 20 | mul r7, r6, 49 // r7:=88200, potential RAW hazard 21 | mov r6, 1 // r6:=1, potential WAR hazard 22 | 23 | lc r0, 2501535 24 | cjmpne r102, r4, r0 // failure 25 | cjmpne r102, r5, 91 // failure 26 | lcs r0, 88200 27 | cjmpne r102, r7, r0 // failure 28 | jmp r103 // success 29 | 30 | failure: 31 | sw r100, 2 32 | jmp r101 // halt 33 | 34 | success: 35 | sw r100, 1 // success 36 | 37 | halt: 38 | hlt 39 | jmp r101 // halt 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2019 by Alex I. Kuznetsov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lxp32-cpu 2 | 3 | LXP32 is a small and FPGA friendly 32-bit CPU IP core based on a simple, original instruction set. Its key features include: 4 | 5 | * portability (described in behavioral VHDL, not tied to any particular vendor); 6 | * 3-stage hazard-free pipeline; 7 | * 256 registers implemented as a RAM block; 8 | * only 30 distinct opcodes; 9 | * separate instruction and data buses, optional instruction cache; 10 | * WISHBONE compatibility; 11 | * 8 interrupts with hardwired priorities; 12 | * optional divider. 13 | 14 | The LXP32 processor was successfully used in commercial projects, is [well documented](https://github.com/lxp32/lxp32-cpu/raw/develop/doc/lxp32-trm.pdf) and comes with a verification environment. 15 | 16 | LXP32 lacks some features of more advanced processors, such as nested interrupt handling, debugging support, floating-point and memory management units. LXP32 ISA (Instruction Set Architecture) does not currently have a C compiler, only assembly based workflow is supported. 17 | 18 | Project website: [https://lxp32.github.io/](https://lxp32.github.io/) 19 | -------------------------------------------------------------------------------- /verify/lxp32/run/xsim/Makefile: -------------------------------------------------------------------------------- 1 | include ../../src/make/sources.make 2 | 3 | ifeq ($(findstring Windows,$(OS)),) 4 | BAT= 5 | else 6 | BAT=.bat 7 | endif 8 | 9 | ######################## 10 | # Phony targets 11 | ######################## 12 | 13 | all: batch 14 | 15 | .PHONY: all compile batch gui clean 16 | 17 | compile: compile.stamp 18 | 19 | batch: compile.stamp 20 | xsim$(BAT) -R tb_sim 21 | 22 | gui: compile.stamp 23 | xsim$(BAT) -g -onfinish stop -onerror stop tb_sim 24 | 25 | clean: 26 | rm -rf .Xil 27 | rm -rf xsim.dir 28 | rm -f webtalk* 29 | rm -f xelab* 30 | rm -f xsim* 31 | rm -f xvhdl* 32 | rm -f hs_err* 33 | rm -f vivado* 34 | rm -f *.wdb 35 | rm -f $(FIRMWARE) 36 | rm -f compile.stamp 37 | 38 | ######################## 39 | # Normal targets 40 | ######################## 41 | 42 | compile.stamp: $(LXP32_RTL) $(COMMON_SRC) $(PLATFORM_RTL) $(TB_SRC) $(FIRMWARE) 43 | xvhdl$(BAT) $(LXP32_RTL) $(COMMON_SRC) $(PLATFORM_RTL) $(TB_SRC) 44 | xelab$(BAT) work.tb -s tb_sim -debug typical 45 | echo > compile.stamp 46 | 47 | %.ram: $(FW_SRC_DIR)/%.asm 48 | $(ASM) -f textio $^ -o $@ 49 | -------------------------------------------------------------------------------- /verify/lxp32/run/ghdl/Makefile: -------------------------------------------------------------------------------- 1 | include ../../src/make/sources.make 2 | 3 | GHDL_FLAGS=--std=93 4 | 5 | WAVE_OUT=wave.fst 6 | 7 | ######################## 8 | # Phony targets 9 | ######################## 10 | 11 | all: batch 12 | 13 | .PHONY: all compile batch gui clean 14 | 15 | .PRECIOUS: $(WAVE_OUT) 16 | 17 | compile: compile.stamp $(FIRMWARE) 18 | 19 | batch: compile.stamp $(FIRMWARE) 20 | ghdl -r $(GHDL_FLAGS) $(TB_MOD) 21 | 22 | gui: $(WAVE_OUT) 23 | gtkwave $(WAVE_OUT) 24 | 25 | clean: 26 | rm -f *.cf 27 | rm -f $(WAVE_OUT) 28 | rm -f *.hier 29 | rm -f $(FIRMWARE) 30 | rm -f *.o 31 | rm -f $(TB_MOD) 32 | rm -f compile.stamp 33 | 34 | ######################## 35 | # Normal targets 36 | ######################## 37 | 38 | $(WAVE_OUT): compile.stamp $(FIRMWARE) 39 | -ghdl -r $(GHDL_FLAGS) $(TB_MOD) --fst=$(WAVE_OUT) 40 | 41 | compile.stamp: $(LXP32_RTL) $(COMMON_SRC) $(PLATFORM_RTL) $(TB_SRC) 42 | ghdl -a $(GHDL_FLAGS) $(LXP32_RTL) $(COMMON_SRC) $(PLATFORM_RTL) $(TB_SRC) 43 | ghdl -e $(GHDL_FLAGS) $(TB_MOD) 44 | echo > compile.stamp 45 | 46 | %.ram: $(FW_SRC_DIR)/%.asm 47 | $(ASM) -f textio $^ -o $@ 48 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test008.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test calculates a CRC-32 checksum of a small byte array 3 | * CRC32("123456789")=0xCBF43926 4 | */ 5 | 6 | lc r100, 0x10000000 // test result output pointer 7 | lc r101, halt 8 | lc r102, failure 9 | 10 | lc r16, 0x10000004 // output pointer 11 | lc r17, 0xFFFFFFFF // initial CRC value 12 | lc r18, 0xEDB88320 // polynom 13 | lc r19, data // input pointer 14 | 15 | lc r32, byte_loop 16 | lc r33, bit_loop 17 | lc r34, dont_xor 18 | 19 | mov r20, 0 // byte counter 20 | 21 | byte_loop: 22 | lub r0, r19 23 | mov r21, 0 // bit counter 24 | 25 | bit_loop: 26 | and r1, r0, 1 27 | and r2, r17, 1 28 | sru r17, r17, 1 29 | xor r3, r1, r2 30 | cjmpe r34, r3, 0 31 | xor r17, r17, r18 32 | 33 | dont_xor: 34 | sru r0, r0, 1 35 | add r21, r21, 1 36 | cjmpul r33, r21, 8 37 | 38 | add r19, r19, 1 39 | add r20, r20, 1 40 | cjmpul r32, r20, 9 41 | 42 | not r17, r17 43 | sw r16, r17 44 | 45 | lc r0, 0xCBF43926 46 | cjmpne r102, r0, r17 47 | 48 | sw r100, 1 49 | jmp r101 50 | 51 | failure: 52 | sw r100, 2 53 | 54 | halt: 55 | hlt 56 | jmp r101 57 | 58 | data: 59 | .byte "123456789" 60 | -------------------------------------------------------------------------------- /tools/src/wigen/range.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module implements members of the Range class. 7 | */ 8 | 9 | #include "range.h" 10 | 11 | #include 12 | 13 | Range::Range(): _valid(false) {} 14 | 15 | Range::Range(int h,int l): _high(h),_low(l),_valid(true) { 16 | if(l>h) throw std::runtime_error("Invalid range"); 17 | } 18 | 19 | void Range::assign(int h,int l) { 20 | if(l>h) throw std::runtime_error("Invalid range"); 21 | _high=h; 22 | _low=l; 23 | _valid=true; 24 | } 25 | 26 | void Range::clear() { 27 | _valid=false; 28 | } 29 | 30 | bool Range::valid() const { 31 | return _valid; 32 | } 33 | 34 | int Range::high() const { 35 | if(!_valid) throw std::runtime_error("Invalid range"); 36 | return _high; 37 | } 38 | 39 | int Range::low() const { 40 | if(!_valid) throw std::runtime_error("Invalid range"); 41 | return _low; 42 | } 43 | 44 | int Range::length() const { 45 | if(!_valid) throw std::runtime_error("Invalid range"); 46 | return _high-_low+1; 47 | } 48 | 49 | std::string Range::toString() const { 50 | if(!_valid) throw std::runtime_error("Invalid range"); 51 | return std::to_string(_high)+" downto "+std::to_string(_low); 52 | } 53 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test010.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies interrupt handling using a simple timer model 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, failure 8 | lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) 9 | lc r104, 0x20000004 // timer: delay between pulses (in cycles) 10 | 11 | lc iv0, timer_handler0 12 | lc iv1, timer_handler1 13 | mov cr, 3 // enable interrupts 0 and 1 14 | 15 | lc r32, 2000 // cycle counter 16 | lc r33, cnt_loop 17 | mov r34, 0 // interrupt 0 call counter 18 | mov r35, 0 // interrupt 1 call counter 19 | 20 | sw r104, 100 21 | sw r103, 10 22 | 23 | cnt_loop: 24 | sub r32, r32, 1 25 | cjmpug r33, r32, 0 // cnt_loop 26 | 27 | cjmpne r102, r34, 10 // failure 28 | cjmpne r102, r35, 4 // failure 29 | 30 | sw r100, 1 31 | jmp r101 // halt 32 | 33 | failure: 34 | sw r100, 2 35 | 36 | halt: 37 | hlt 38 | jmp r101 // halt 39 | 40 | timer_handler0: 41 | add r34, r34, 1 42 | lc r0, 0x10000004 43 | sw r0, r34 44 | cjmpne irp, r34, 5 // exit interrupt handler if r34!=5 45 | mov cr, 1 // disable interrupt 1 46 | iret 47 | 48 | timer_handler1: 49 | add r35, r35, 1 50 | // Interrupt 1 has lower priority than interrupt 0 and will be called later 51 | cjmpne r102, r34, r35 52 | lc r0, 0x10000008 53 | sw r0, r35 54 | iret 55 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test015.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * Test unconventional interrupt handlers 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, 0x30000000 // coprocessor input register 8 | lc r103, 0x30000004 // coprocessor output register 9 | lc r104, failure 10 | 11 | // Initialize interrupt handlers 12 | lc iv2, coprocessor_handler 13 | mov cr, 4 // enable interrupts from the coprocessor 14 | lc r110, interrupt_exit@1 // '1' in the LSB is an interrupt exit flag 15 | 16 | // Initialize random generator 17 | mov r64, 1 // initial PRBS value 18 | lc r65, 1103515245 // PRBS multiplier 19 | lc r66, 12345 // PRBS addition constant 20 | lc r67, 32767 // PRBS mask 21 | 22 | // Main loop 23 | lc r32, loop 24 | lc r33, rand 25 | lc r34, 2000 26 | 27 | loop: 28 | call r33 29 | cjmpe r32, r0, 0 // if(r==0) continue; 30 | sw r102, r0 31 | hlt 32 | 33 | interrupt_exit: 34 | lw r1, r103 35 | mul r0, r0, 3 36 | cjmpne r104, r0, r1 // failure 37 | 38 | sub r34, r34, 1 39 | cjmpug r32, r34, 0 // loop 40 | 41 | sw r100, 1 42 | jmp r101 // halt 43 | 44 | failure: 45 | sw r100, 2 46 | 47 | halt: 48 | hlt 49 | jmp r101 // halt 50 | 51 | rand: 52 | mul r64, r64, r65 53 | add r64, r64, r66 54 | sru r0, r64, 16 55 | and r0, r0, r67 56 | ret 57 | 58 | coprocessor_handler: 59 | jmp r110 // exit to a given point, ignore irp 60 | -------------------------------------------------------------------------------- /rtl/lxp32_compl.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Complementor 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Computes a 2's complement of its input. Used as an auxiliary 9 | -- unit in the divider. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; 15 | 16 | entity lxp32_compl is 17 | port( 18 | clk_i: in std_logic; 19 | compl_i: in std_logic; 20 | d_i: in std_logic_vector(31 downto 0); 21 | d_o: out std_logic_vector(31 downto 0) 22 | ); 23 | end entity; 24 | 25 | architecture rtl of lxp32_compl is 26 | 27 | signal d_prepared: unsigned(d_i'range); 28 | signal sum_low: unsigned(16 downto 0); 29 | signal d_high: unsigned(15 downto 0); 30 | signal sum_high: unsigned(15 downto 0); 31 | 32 | begin 33 | 34 | d_prepared_gen: for i in d_prepared'range generate 35 | d_prepared(i)<=d_i(i) xor compl_i; 36 | end generate; 37 | 38 | process (clk_i) is 39 | begin 40 | if rising_edge(clk_i) then 41 | sum_low<=("0"&d_prepared(15 downto 0))+(to_unsigned(0,16)&compl_i); 42 | d_high<=d_prepared(31 downto 16); 43 | end if; 44 | end process; 45 | 46 | sum_high<=d_high+(to_unsigned(0,15)&sum_low(sum_low'high)); 47 | 48 | d_o<=std_logic_vector(sum_high&sum_low(15 downto 0)); 49 | 50 | end architecture; 51 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/scrambler.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Scrambler 3 | -- 4 | -- Part of the LXP32 test platform 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Generates a pseudo-random binary sequence using a Linear-Feedback 9 | -- Shift Register (LFSR). 10 | -- 11 | -- In order to generate a maximum-length sequence, 1+x^TAP1+x^TAP2 12 | -- must be a primitive polynomial. Typical polynomials include: 13 | -- (6,7), (9,11), (14,15). 14 | -- 15 | -- Note: regardless of whether this description is synthesizable, 16 | -- it was designed exclusively for simulation purposes. 17 | --------------------------------------------------------------------- 18 | 19 | library ieee; 20 | use ieee.std_logic_1164.all; 21 | 22 | entity scrambler is 23 | generic( 24 | TAP1: integer; 25 | TAP2: integer 26 | ); 27 | port( 28 | clk_i: in std_logic; 29 | rst_i: in std_logic; 30 | ce_i: in std_logic; 31 | d_o: out std_logic 32 | ); 33 | end entity; 34 | 35 | architecture rtl of scrambler is 36 | 37 | signal reg: std_logic_vector(TAP2 downto 1):=(others=>'1'); 38 | 39 | begin 40 | 41 | process (clk_i) is 42 | begin 43 | if rising_edge(clk_i) then 44 | if rst_i='1' then 45 | reg<=(others=>'1'); 46 | elsif ce_i='1' then 47 | reg<=reg(TAP2-1 downto 1)&(reg(TAP2) xor reg(TAP1)); 48 | end if; 49 | end if; 50 | end process; 51 | 52 | d_o<=reg(1); 53 | 54 | end architecture; 55 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/linker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the Linker class which performs 7 | * linking of LXP32 binary objects. 8 | */ 9 | 10 | #ifndef LINKER_H_INCLUDED 11 | #define LINKER_H_INCLUDED 12 | 13 | #include "linkableobject.h" 14 | #include "outputwriter.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | class Linker { 23 | struct GlobalSymbolData { 24 | LinkableObject *obj=nullptr; 25 | LinkableObject::Word rva=0; 26 | std::set refs; 27 | }; 28 | 29 | std::vector _objects; 30 | LinkableObject *_entryObject=nullptr; 31 | std::map _globalSymbolTable; 32 | 33 | // Various output options 34 | LinkableObject::Word _base=0; 35 | std::size_t _align=4; 36 | std::size_t _imageSize=0; 37 | std::size_t _bytesWritten=0; 38 | public: 39 | void addObject(LinkableObject &obj); 40 | void link(OutputWriter &writer); 41 | void setBase(LinkableObject::Word base); 42 | void setAlignment(std::size_t align); 43 | void setImageSize(std::size_t size); 44 | void generateMap(std::ostream &s); 45 | private: 46 | void buildSymbolTable(); 47 | void placeObjects(); 48 | void relocateObject(LinkableObject *obj); 49 | void writeObjects(OutputWriter &writer); 50 | void markAsUsed(const LinkableObject *obj,std::set &used); 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /tools/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3.0) 2 | project(lxp32tools) 3 | 4 | # Examine environment 5 | 6 | if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID STREQUAL Clang) 7 | set(GNU_SYNTAX TRUE) 8 | endif() 9 | 10 | # Set default install prefix if not manually set by the user 11 | 12 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 13 | set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../bin" CACHE PATH "Install prefix" FORCE) 14 | endif() 15 | 16 | message("Install prefix: ${CMAKE_INSTALL_PREFIX}") 17 | 18 | # Enable C++11 19 | 20 | set(CMAKE_CXX_STANDARD 11) 21 | 22 | # Set up warning level for GCC/Clang 23 | 24 | if(GNU_SYNTAX) 25 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra") 26 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") 27 | endif() 28 | 29 | # On Windows, link runtime statically 30 | 31 | if(WIN32) 32 | if(GNU_SYNTAX) 33 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") 34 | elseif(MSVC) 35 | foreach(flag_var 36 | CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE 37 | CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO 38 | CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE 39 | CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO 40 | ) 41 | string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 42 | endforeach(flag_var) 43 | endif() 44 | endif() 45 | 46 | # Build targets 47 | 48 | add_subdirectory(lxp32asm) 49 | add_subdirectory(lxp32dump) 50 | add_subdirectory(wigen) 51 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test005.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies bytewise DBUS access 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, failure 8 | 9 | lc r16, 0x10000004 // output pointer 10 | lc r17, data // input pointer 11 | 12 | // Check for bytewise read 13 | lc r18, 0xbc 14 | lc r19, 0x9a 15 | lc r20, 0x78 16 | lc r21, 0x56 17 | lc r22, 0xffffffbc 18 | lc r23, 0xffffff9a 19 | lc r24, 0x78 20 | lc r25, 0x56 21 | 22 | lub r0, r17 23 | sw r16, r0 24 | cjmpne r102, r0, r18 25 | add r17, r17, 1 26 | lub r0, r17 27 | sw r16, r0 28 | cjmpne r102, r0, r19 29 | add r17, r17, 1 30 | lub r0, r17 31 | sw r16, r0 32 | cjmpne r102, r0, r20 33 | add r17, r17, 1 34 | lub r0, r17 35 | sw r16, r0 36 | cjmpne r102, r0, r21 37 | sub r17, r17, 3 38 | lsb r0, r17 39 | sw r16, r0 40 | cjmpne r102, r0, r22 41 | add r17, r17, 1 42 | lsb r0, r17 43 | sw r16, r0 44 | cjmpne r102, r0, r23 45 | add r17, r17, 1 46 | lsb r0, r17 47 | sw r16, r0 48 | cjmpne r102, r0, r24 49 | add r17, r17, 1 50 | lsb r0, r17 51 | sw r16, r0 52 | cjmpne r102, r0, r25 53 | 54 | // Check for bytewise write 55 | lc r17, 0x00008004 56 | sb r17, 0x12 57 | add r17, r17, 1 58 | sb r17, 0x34 59 | add r17, r17, 1 60 | sb r17, 0x56 61 | add r17, r17, 1 62 | sb r17, 0x78 63 | 64 | // Read the whole word and compare 65 | sub r17, r17, 3 66 | lw r0, r17 67 | lc r18, 0x78563412 68 | cjmpne r102, r0, r18 69 | 70 | sw r100, 1 71 | jmp r101 72 | 73 | failure: 74 | sw r100, 2 75 | 76 | halt: 77 | hlt 78 | jmp r101 79 | 80 | data: 81 | .word 0x56789ABC 82 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/outputwriter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the OutputWriter abstract class and its 7 | * derived classes. These classes are used to write LXP32 executable 8 | * code in different formats. 9 | */ 10 | 11 | #ifndef OUTPUTWRITER_H_INCLUDED 12 | #define OUTPUTWRITER_H_INCLUDED 13 | 14 | #include 15 | #include 16 | 17 | /* 18 | * An abstract base class for all writers 19 | */ 20 | 21 | class OutputWriter { 22 | std::size_t _size=0; 23 | public: 24 | virtual ~OutputWriter() {} 25 | virtual void write(const char *data,std::size_t n); 26 | virtual void abort() {} 27 | void pad(std::size_t size); 28 | std::size_t size() const; 29 | protected: 30 | virtual void writeData(const char *data,std::size_t n)=0; 31 | }; 32 | 33 | /* 34 | * Write a regular binary file 35 | */ 36 | 37 | class BinaryOutputWriter : public OutputWriter { 38 | std::string _filename; 39 | std::ofstream _os; 40 | public: 41 | BinaryOutputWriter(const std::string &filename); 42 | virtual void abort() override; 43 | protected: 44 | virtual void writeData(const char *data,std::size_t n) override; 45 | }; 46 | 47 | /* 48 | * Write a text file (one word per line) 49 | */ 50 | 51 | class TextOutputWriter : public OutputWriter { 52 | public: 53 | enum Format {Bin,Dec,Hex}; 54 | private: 55 | std::string _filename; 56 | std::ofstream _os; 57 | std::string _buf; 58 | Format _fmt; 59 | public: 60 | TextOutputWriter(const std::string &filename,Format f); 61 | ~TextOutputWriter(); 62 | virtual void abort() override; 63 | protected: 64 | virtual void writeData(const char *data,std::size_t n) override; 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test002.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test calculates a few Fibonacci sequence members 3 | * end compares them to pre-calculated values. 4 | */ 5 | 6 | lc r100, 0x10000000 // test result output pointer 7 | lc r101, halt 8 | 9 | // Calculate Fibonacci sequence members 10 | mov r16, 0 // current member 11 | mov r17, 1 // next member 12 | lc r18, 0 // counter 13 | lc r19, 0x00008000 // destination pointer 14 | lc r32, calc_loop 15 | 16 | calc_loop: 17 | sw r19, r16 18 | add r19, r19, 4 19 | add r18, r18, 1 20 | add r0, r16, r17 21 | mov r16, r17 22 | mov r17, r0 23 | cjmpul r32, r18, 40 24 | 25 | // Compare 26 | lc r16, 0x00008000 27 | lc r17, expected 28 | mov r18, 0 // counter 29 | lc r32, comp_loop 30 | lc r33, comp_differ 31 | 32 | comp_loop: 33 | lw r0, r16 34 | lw r1, r17 35 | cjmpne r33, r0, r1 36 | add r16, r16, 4 37 | add r17, r17, 4 38 | add r18, r18, 1 39 | cjmpul r32, r18, 40 40 | 41 | // Everything seems to be OK 42 | sw r100, 1 43 | 44 | halt: 45 | hlt 46 | jmp r101 47 | 48 | comp_differ: 49 | sw r100, 2 50 | jmp r101 51 | 52 | // Expected (pre-calculated) values 53 | expected: 54 | .word 0 55 | .word 1 56 | .word 1 57 | .word 2 58 | .word 3 59 | .word 5 60 | .word 8 61 | .word 13 62 | .word 21 63 | .word 34 64 | .word 55 65 | .word 89 66 | .word 144 67 | .word 233 68 | .word 377 69 | .word 610 70 | .word 987 71 | .word 1597 72 | .word 2584 73 | .word 4181 74 | .word 6765 75 | .word 10946 76 | .word 17711 77 | .word 28657 78 | .word 46368 79 | .word 75025 80 | .word 121393 81 | .word 196418 82 | .word 317811 83 | .word 514229 84 | .word 832040 85 | .word 1346269 86 | .word 2178309 87 | .word 3524578 88 | .word 5702887 89 | .word 9227465 90 | .word 14930352 91 | .word 24157817 92 | .word 39088169 93 | .word 63245986 94 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module declares the members of the Utils namespace. 7 | */ 8 | 9 | #ifndef UTILS_H_INCLUDED 10 | #define UTILS_H_INCLUDED 11 | 12 | #include 13 | #include 14 | 15 | namespace Utils { 16 | template std::string hex(const T &w) { 17 | static_assert(std::is_integral::value,"Argument must be of integral type"); 18 | const char *hexstr="0123456789ABCDEF"; 19 | std::string res; 20 | 21 | res.reserve(sizeof(T)*2); 22 | 23 | for(int i=sizeof(T)*8-4;i>=0;i-=4) { 24 | res.push_back(hexstr[(w>>i)&0x0F]); 25 | } 26 | return res; 27 | } 28 | 29 | template std::string bin(const T &w) { 30 | static_assert(std::is_integral::value,"Argument must be of integral type"); 31 | std::string res; 32 | 33 | res.reserve(sizeof(T)*8); 34 | 35 | for(int i=sizeof(T)*8-1;i>=0;i--) { 36 | if(((w>>i)&1)!=0) res.push_back('1'); 37 | else res.push_back('0'); 38 | } 39 | return res; 40 | } 41 | 42 | std::string urlEncode(const std::string &str); 43 | std::string urlDecode(const std::string &str); 44 | 45 | std::string normalizeSeparators(const std::string &path); 46 | std::string nativeSeparators(const std::string &path); 47 | bool isAbsolutePath(const std::string &path); 48 | bool fileExists(const std::string &path); 49 | std::string relativePath(const std::string &from,const std::string &to); 50 | 51 | std::string dequoteString(const std::string &str); 52 | 53 | bool ishexdigit(char ch); 54 | bool isoctdigit(char ch); 55 | 56 | template bool isPowerOf2(const T &x) { 57 | static_assert(std::is_integral::value,"Argument must be of integral type"); 58 | return (x!=0)&&((x&(x-1))==0); 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /verify/lxp32/src/tb/tb_pkg.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32 testbench package 3 | -- 4 | -- Part of the LXP32 testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Auxiliary package declaration for the LXP32 testbench 9 | --------------------------------------------------------------------- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | 14 | package tb_pkg is 15 | constant c_max_program_size: integer:=8192; 16 | 17 | type soc_globals_type is record 18 | rst_i: std_logic; 19 | cpu_rst_i: std_logic; 20 | end record; 21 | 22 | type soc_wbs_in_type is record 23 | cyc: std_logic; 24 | stb: std_logic; 25 | we: std_logic; 26 | sel: std_logic_vector(3 downto 0); 27 | adr: std_logic_vector(31 downto 2); 28 | dat: std_logic_vector(31 downto 0); 29 | end record; 30 | 31 | type soc_wbs_out_type is record 32 | ack: std_logic; 33 | dat: std_logic_vector(31 downto 0); 34 | end record; 35 | 36 | type soc_wbm_in_type is record 37 | ack: std_logic; 38 | dat: std_logic_vector(31 downto 0); 39 | end record; 40 | 41 | type soc_wbm_out_type is record 42 | cyc: std_logic; 43 | stb: std_logic; 44 | we: std_logic; 45 | sel: std_logic_vector(3 downto 0); 46 | adr: std_logic_vector(27 downto 2); 47 | dat: std_logic_vector(31 downto 0); 48 | end record; 49 | 50 | type monitor_out_type is record 51 | data: std_logic_vector(31 downto 0); 52 | valid: std_logic; 53 | end record; 54 | 55 | procedure load_ram( 56 | filename: string; 57 | signal clk: in std_logic; 58 | signal soc_in: out soc_wbs_in_type; 59 | signal soc_out: in soc_wbs_out_type 60 | ); 61 | 62 | procedure run_test( 63 | filename: string; 64 | signal clk: in std_logic; 65 | signal globals: out soc_globals_type; 66 | signal soc_in: out soc_wbs_in_type; 67 | signal soc_out: in soc_wbs_out_type; 68 | signal result: in monitor_out_type 69 | ); 70 | end package; 71 | -------------------------------------------------------------------------------- /rtl/lxp32_mul_seq.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Sequential multiplier 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- The smallest possible multiplier. Implemented using 9 | -- an accumulator. One multiplication takes 34 cycles. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; 15 | 16 | entity lxp32_mul_seq is 17 | port( 18 | clk_i: in std_logic; 19 | rst_i: in std_logic; 20 | ce_i: in std_logic; 21 | op1_i: in std_logic_vector(31 downto 0); 22 | op2_i: in std_logic_vector(31 downto 0); 23 | ce_o: out std_logic; 24 | result_o: out std_logic_vector(31 downto 0) 25 | ); 26 | end entity; 27 | 28 | architecture rtl of lxp32_mul_seq is 29 | 30 | signal reg1: unsigned(op1_i'range); 31 | signal reg2: unsigned(op2_i'range); 32 | signal pp: unsigned(31 downto 0); 33 | signal acc_sum: unsigned(31 downto 0); 34 | signal cnt: integer range 0 to 32:=0; 35 | signal ceo: std_logic:='0'; 36 | 37 | begin 38 | 39 | pp<=reg1 when reg2(0)='1' else (others=>'0'); 40 | 41 | process (clk_i) is 42 | begin 43 | if rising_edge(clk_i) then 44 | if rst_i='1' then 45 | ceo<='0'; 46 | cnt<=0; 47 | reg1<=(others=>'-'); 48 | reg2<=(others=>'-'); 49 | acc_sum<=(others=>'-'); 50 | else 51 | if cnt=1 then 52 | ceo<='1'; 53 | else 54 | ceo<='0'; 55 | end if; 56 | 57 | if ce_i='1' then 58 | cnt<=32; 59 | reg1<=unsigned(op1_i); 60 | reg2<=unsigned(op2_i); 61 | acc_sum<=(others=>'0'); 62 | else 63 | acc_sum<=acc_sum+pp; 64 | reg1<=reg1(reg1'high-1 downto 0)&"0"; 65 | reg2<="0"®2(reg2'high downto 1); 66 | if cnt>0 then 67 | cnt<=cnt-1; 68 | end if; 69 | end if; 70 | end if; 71 | end if; 72 | end process; 73 | 74 | result_o<=std_logic_vector(acc_sum); 75 | ce_o<=ceo; 76 | 77 | end architecture; 78 | -------------------------------------------------------------------------------- /rtl/lxp32_ram256x32.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Generic dual-port memory 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Portable description of a dual-port memory block with one write 9 | -- port. Major FPGA synthesis tools can infer on-chip block RAM 10 | -- from this description. Can be replaced with a library component 11 | -- wrapper if needed. 12 | --------------------------------------------------------------------- 13 | 14 | library ieee; 15 | use ieee.std_logic_1164.all; 16 | use ieee.numeric_std.all; 17 | 18 | entity lxp32_ram256x32 is 19 | port( 20 | clk_i: in std_logic; 21 | 22 | we_i: in std_logic; 23 | waddr_i: in std_logic_vector(7 downto 0); 24 | wdata_i: in std_logic_vector(31 downto 0); 25 | 26 | re_i: in std_logic; 27 | raddr_i: in std_logic_vector(7 downto 0); 28 | rdata_o: out std_logic_vector(31 downto 0) 29 | ); 30 | end entity; 31 | 32 | architecture rtl of lxp32_ram256x32 is 33 | 34 | type ram_type is array(255 downto 0) of std_logic_vector(31 downto 0); 35 | signal ram: ram_type:=(others=>(others=>'0')); -- zero-initialize for SRAM-based FPGAs 36 | 37 | attribute syn_ramstyle: string; 38 | attribute syn_ramstyle of ram: signal is "no_rw_check"; 39 | attribute ram_style: string; -- for Xilinx 40 | attribute ram_style of ram: signal is "block"; 41 | 42 | begin 43 | 44 | -- Write port 45 | 46 | process (clk_i) is 47 | begin 48 | if rising_edge(clk_i) then 49 | if we_i='1' then 50 | ram(to_integer(unsigned(waddr_i)))<=wdata_i; 51 | end if; 52 | end if; 53 | end process; 54 | 55 | -- Read port 56 | 57 | process (clk_i) is 58 | begin 59 | if rising_edge(clk_i) then 60 | if re_i='1' then 61 | if is_x(raddr_i) then -- to avoid numeric_std warnings during simulation 62 | rdata_o<=(others=>'X'); 63 | else 64 | rdata_o<=ram(to_integer(unsigned(raddr_i))); 65 | end if; 66 | end if; 67 | end if; 68 | end process; 69 | 70 | end architecture; 71 | -------------------------------------------------------------------------------- /rtl/lxp32_ubuf.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Microbuffer 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- A small buffer with a FIFO-like interface, implemented 9 | -- using registers. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | 15 | entity lxp32_ubuf is 16 | generic( 17 | DATA_WIDTH: integer 18 | ); 19 | port( 20 | clk_i: in std_logic; 21 | rst_i: in std_logic; 22 | 23 | we_i: in std_logic; 24 | d_i: in std_logic_vector(DATA_WIDTH-1 downto 0); 25 | re_i: in std_logic; 26 | d_o: out std_logic_vector(DATA_WIDTH-1 downto 0); 27 | 28 | empty_o: out std_logic; 29 | full_o: out std_logic 30 | ); 31 | end entity; 32 | 33 | architecture rtl of lxp32_ubuf is 34 | 35 | signal we: std_logic; 36 | signal re: std_logic; 37 | 38 | signal empty: std_logic:='1'; 39 | signal full: std_logic:='0'; 40 | 41 | type regs_type is array (1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0); 42 | signal regs: regs_type; 43 | signal regs_mux: regs_type; 44 | 45 | begin 46 | 47 | we<=we_i and not full; 48 | re<=re_i and not empty; 49 | 50 | process (clk_i) is 51 | begin 52 | if rising_edge(clk_i) then 53 | if rst_i='1' then 54 | empty<='1'; 55 | full<='0'; 56 | regs<=(others=>(others=>'-')); 57 | else 58 | if re='0' then 59 | regs(0)<=regs_mux(0); 60 | else 61 | regs(0)<=regs_mux(1); 62 | end if; 63 | 64 | regs(1)<=regs_mux(1); 65 | 66 | if we='1' and re='0' then 67 | empty<='0'; 68 | full<=not empty; 69 | elsif we='0' and re='1' then 70 | empty<=not full; 71 | full<='0'; 72 | end if; 73 | end if; 74 | end if; 75 | end process; 76 | 77 | regs_mux(0)<=regs(0) when we='0' or empty='0' else d_i; 78 | regs_mux(1)<=regs(1) when we='0' or empty='1' else d_i; 79 | 80 | d_o<=regs(0); 81 | empty_o<=empty; 82 | full_o<=full; 83 | 84 | end architecture; 85 | -------------------------------------------------------------------------------- /verify/common_pkg/common_pkg_body.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Common package for LXP32 testbenches 3 | -- 4 | -- Part of the LXP32 verification environment 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | --------------------------------------------------------------------- 8 | 9 | library ieee; 10 | use ieee.std_logic_1164.all; 11 | use ieee.numeric_std.all; 12 | use ieee.math_real.all; 13 | 14 | package body common_pkg is 15 | procedure rand(variable st: inout rng_state_type; a,b: integer; variable x: out integer) is 16 | variable r: real; 17 | begin 18 | assert a<=b report "Invalid range" severity failure; 19 | uniform(st.seed1,st.seed2,r); 20 | r:=r*real(b-a+1); 21 | x:=a+integer(floor(r)); 22 | end procedure; 23 | 24 | function hex_string(x: std_logic_vector) return string is 25 | variable xx: std_logic_vector(x'length-1 downto 0); 26 | variable i: integer:=0; 27 | variable ii: integer; 28 | variable c: integer; 29 | variable high_index: integer; 30 | variable s: string(x'length downto 1); 31 | begin 32 | xx:=x; 33 | loop 34 | ii:=i*4; 35 | exit when ii>xx'high; 36 | if ii+3<=xx'high then 37 | high_index:=ii+3; 38 | else 39 | high_index:=xx'high; 40 | end if; 41 | 42 | if is_x(xx(high_index downto ii)) then 43 | c:=-1; 44 | else 45 | c:=to_integer(unsigned(xx(high_index downto ii))); 46 | end if; 47 | 48 | case c is 49 | when 0 => s(i+1):='0'; 50 | when 1 => s(i+1):='1'; 51 | when 2 => s(i+1):='2'; 52 | when 3 => s(i+1):='3'; 53 | when 4 => s(i+1):='4'; 54 | when 5 => s(i+1):='5'; 55 | when 6 => s(i+1):='6'; 56 | when 7 => s(i+1):='7'; 57 | when 8 => s(i+1):='8'; 58 | when 9 => s(i+1):='9'; 59 | when 10 => s(i+1):='A'; 60 | when 11 => s(i+1):='B'; 61 | when 12 => s(i+1):='C'; 62 | when 13 => s(i+1):='D'; 63 | when 14 => s(i+1):='E'; 64 | when 15 => s(i+1):='F'; 65 | when others => s(i+1):='X'; 66 | end case; 67 | 68 | i:=i+1; 69 | end loop; 70 | return s(i downto 1); 71 | end function; 72 | end package body; 73 | -------------------------------------------------------------------------------- /rtl/lxp32_mul_dsp.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- DSP multiplier 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- This multiplier is designed for technologies that provide fast 9 | -- 16x16 multipliers, including most modern FPGA families. One 10 | -- multiplication takes 2 cycles. 11 | --------------------------------------------------------------------- 12 | 13 | library ieee; 14 | use ieee.std_logic_1164.all; 15 | use ieee.numeric_std.all; 16 | 17 | entity lxp32_mul_dsp is 18 | port( 19 | clk_i: in std_logic; 20 | rst_i: in std_logic; 21 | ce_i: in std_logic; 22 | op1_i: in std_logic_vector(31 downto 0); 23 | op2_i: in std_logic_vector(31 downto 0); 24 | ce_o: out std_logic; 25 | result_o: out std_logic_vector(31 downto 0) 26 | ); 27 | end entity; 28 | 29 | architecture rtl of lxp32_mul_dsp is 30 | 31 | signal pp00: std_logic_vector(31 downto 0); 32 | signal pp01: std_logic_vector(31 downto 0); 33 | signal pp10: std_logic_vector(31 downto 0); 34 | 35 | signal product: unsigned(31 downto 0); 36 | 37 | signal ceo: std_logic:='0'; 38 | 39 | begin 40 | 41 | mul00_inst: entity work.lxp32_mul16x16 42 | port map( 43 | clk_i=>clk_i, 44 | a_i=>op1_i(15 downto 0), 45 | b_i=>op2_i(15 downto 0), 46 | p_o=>pp00 47 | ); 48 | 49 | mul01_inst: entity work.lxp32_mul16x16 50 | port map( 51 | clk_i=>clk_i, 52 | a_i=>op1_i(15 downto 0), 53 | b_i=>op2_i(31 downto 16), 54 | p_o=>pp01 55 | ); 56 | 57 | mul10_inst: entity work.lxp32_mul16x16 58 | port map( 59 | clk_i=>clk_i, 60 | a_i=>op1_i(31 downto 16), 61 | b_i=>op2_i(15 downto 0), 62 | p_o=>pp10 63 | ); 64 | 65 | product(31 downto 16)<=unsigned(pp00(31 downto 16))+unsigned(pp01(15 downto 0))+unsigned(pp10(15 downto 0)); 66 | product(15 downto 0)<=unsigned(pp00(15 downto 0)); 67 | result_o<=std_logic_vector(product); 68 | 69 | process (clk_i) is 70 | begin 71 | if rising_edge(clk_i) then 72 | if rst_i='1' then 73 | ceo<='0'; 74 | else 75 | ceo<=ce_i; 76 | end if; 77 | end if; 78 | end process; 79 | 80 | ce_o<=ceo; 81 | 82 | end architecture; 83 | -------------------------------------------------------------------------------- /verify/lxp32/src/make/sources.make: -------------------------------------------------------------------------------- 1 | # CPU RTL 2 | 3 | LXP32_DIR=../../../../rtl 4 | LXP32_RTL=$(LXP32_DIR)/lxp32_mul16x16.vhd\ 5 | $(LXP32_DIR)/lxp32_mul_dsp.vhd\ 6 | $(LXP32_DIR)/lxp32_mul_opt.vhd\ 7 | $(LXP32_DIR)/lxp32_mul_seq.vhd\ 8 | $(LXP32_DIR)/lxp32_compl.vhd\ 9 | $(LXP32_DIR)/lxp32_divider.vhd\ 10 | $(LXP32_DIR)/lxp32_shifter.vhd\ 11 | $(LXP32_DIR)/lxp32_alu.vhd\ 12 | $(LXP32_DIR)/lxp32_dbus.vhd\ 13 | $(LXP32_DIR)/lxp32_execute.vhd\ 14 | $(LXP32_DIR)/lxp32_decode.vhd\ 15 | $(LXP32_DIR)/lxp32_ubuf.vhd\ 16 | $(LXP32_DIR)/lxp32_fetch.vhd\ 17 | $(LXP32_DIR)/lxp32_ram256x32.vhd\ 18 | $(LXP32_DIR)/lxp32_interrupt_mux.vhd\ 19 | $(LXP32_DIR)/lxp32_scratchpad.vhd\ 20 | $(LXP32_DIR)/lxp32_cpu.vhd\ 21 | $(LXP32_DIR)/lxp32u_top.vhd\ 22 | $(LXP32_DIR)/lxp32_icache.vhd\ 23 | $(LXP32_DIR)/lxp32c_top.vhd 24 | 25 | # Common package 26 | 27 | COMMON_PKG_DIR=../../../common_pkg 28 | COMMON_SRC=$(COMMON_PKG_DIR)/common_pkg.vhd $(COMMON_PKG_DIR)/common_pkg_body.vhd 29 | 30 | # Platform RTL 31 | 32 | PLATFORM_DIR=../../src/platform 33 | PLATFORM_RTL=$(PLATFORM_DIR)/generic_dpram.vhd\ 34 | $(PLATFORM_DIR)/scrambler.vhd\ 35 | $(PLATFORM_DIR)/dbus_monitor.vhd\ 36 | $(PLATFORM_DIR)/program_ram.vhd\ 37 | $(PLATFORM_DIR)/timer.vhd\ 38 | $(PLATFORM_DIR)/coprocessor.vhd\ 39 | $(PLATFORM_DIR)/intercon.vhd\ 40 | $(PLATFORM_DIR)/ibus_adapter.vhd\ 41 | $(PLATFORM_DIR)/platform.vhd 42 | 43 | # Testbench sources 44 | 45 | COMMON_PKG_DIR=../../../common_pkg 46 | TB_DIR=../../src/tb 47 | TB_SRC=$(TB_DIR)/tb_pkg.vhd\ 48 | $(TB_DIR)/tb_pkg_body.vhd\ 49 | $(TB_DIR)/monitor.vhd\ 50 | $(TB_DIR)/tb.vhd 51 | 52 | TB_MOD=tb 53 | 54 | # Firmware 55 | 56 | FW_SRC_DIR=../../src/firmware 57 | FIRMWARE=test001.ram\ 58 | test002.ram\ 59 | test003.ram\ 60 | test004.ram\ 61 | test005.ram\ 62 | test006.ram\ 63 | test007.ram\ 64 | test008.ram\ 65 | test009.ram\ 66 | test010.ram\ 67 | test011.ram\ 68 | test012.ram\ 69 | test013.ram\ 70 | test014.ram\ 71 | test015.ram\ 72 | test016.ram\ 73 | test017.ram\ 74 | test018.ram\ 75 | test019.ram\ 76 | test020.ram\ 77 | test021.ram\ 78 | test022.ram 79 | 80 | # LXP32 assembler executable 81 | 82 | ASM=../../../../tools/bin/lxp32asm 83 | -------------------------------------------------------------------------------- /verify/lxp32/src/tb/monitor.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Test monitor 3 | -- 4 | -- Part of the LXP32 testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Provide means for a test platform to interact with the testbench. 9 | --------------------------------------------------------------------- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; 14 | 15 | use work.common_pkg.all; 16 | use work.tb_pkg.all; 17 | 18 | entity monitor is 19 | generic( 20 | VERBOSE: boolean 21 | ); 22 | port( 23 | clk_i: in std_logic; 24 | rst_i: in std_logic; 25 | 26 | wbs_cyc_i: in std_logic; 27 | wbs_stb_i: in std_logic; 28 | wbs_we_i: in std_logic; 29 | wbs_sel_i: in std_logic_vector(3 downto 0); 30 | wbs_ack_o: out std_logic; 31 | wbs_adr_i: in std_logic_vector(27 downto 2); 32 | wbs_dat_i: in std_logic_vector(31 downto 0); 33 | wbs_dat_o: out std_logic_vector(31 downto 0); 34 | 35 | finished_o: out std_logic; 36 | result_o: out std_logic_vector(31 downto 0) 37 | ); 38 | end entity; 39 | 40 | architecture sim of monitor is 41 | 42 | signal result: std_logic_vector(31 downto 0):=(others=>'0'); 43 | signal finished: std_logic:='0'; 44 | 45 | begin 46 | 47 | wbs_ack_o<=wbs_cyc_i and wbs_stb_i; 48 | wbs_dat_o<=(others=>'0'); 49 | 50 | finished_o<=finished; 51 | result_o<=result; 52 | 53 | process (clk_i) is 54 | begin 55 | if rising_edge(clk_i) then 56 | if rst_i='1' then 57 | finished<='0'; 58 | result<=(others=>'0'); 59 | elsif wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' then 60 | assert wbs_sel_i="1111" 61 | report "Monitor doesn't support byte-granular access "& 62 | "(SEL_I() is 0x"&hex_string(wbs_sel_i)&")" 63 | severity failure; 64 | 65 | if VERBOSE then 66 | report "Monitor: value "& 67 | "0x"&hex_string(wbs_dat_i)& 68 | " written to address "& 69 | "0x"&hex_string(wbs_adr_i); 70 | end if; 71 | 72 | if unsigned(wbs_adr_i)=to_unsigned(0,wbs_adr_i'length) then 73 | result<=wbs_dat_i; 74 | finished<='1'; 75 | end if; 76 | end if; 77 | end if; 78 | end process; 79 | 80 | end architecture; 81 | -------------------------------------------------------------------------------- /verify/icache/src/tb/ram_model.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- RAM model 3 | -- 4 | -- Part of the LXP32 instruction cache testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Simulates RAM controller which provides WISHBONE registered 9 | -- feedback interface. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; 15 | 16 | use work.common_pkg.all; 17 | use work.tb_pkg.all; 18 | 19 | entity ram_model is 20 | port( 21 | clk_i: in std_logic; 22 | 23 | wbm_cyc_i: in std_logic; 24 | wbm_stb_i: in std_logic; 25 | wbm_cti_i: in std_logic_vector(2 downto 0); 26 | wbm_bte_i: in std_logic_vector(1 downto 0); 27 | wbm_ack_o: out std_logic; 28 | wbm_adr_i: in std_logic_vector(29 downto 0); 29 | wbm_dat_o: out std_logic_vector(31 downto 0) 30 | ); 31 | end entity; 32 | 33 | architecture sim of ram_model is 34 | 35 | signal ack: std_logic:='0'; 36 | signal cycle: std_logic:='0'; 37 | 38 | begin 39 | 40 | wbm_ack_o<=ack; 41 | 42 | process (clk_i) is 43 | begin 44 | if rising_edge(clk_i) then 45 | if wbm_cyc_i='1' and wbm_stb_i='1' and wbm_cti_i="010" and wbm_bte_i="00" then 46 | cycle<='1'; 47 | elsif wbm_cyc_i='0' or (wbm_cyc_i='1' and wbm_stb_i='1' and (wbm_cti_i/="010" or wbm_bte_i/="00")) then 48 | cycle<='0'; 49 | end if; 50 | end if; 51 | end process; 52 | 53 | process is 54 | variable rng_state: rng_state_type; 55 | variable delay: integer; 56 | begin 57 | wait until rising_edge(clk_i) and wbm_cyc_i='1' and wbm_stb_i='1'; 58 | ack<='0'; 59 | 60 | -- Random delay before the first beat 61 | if cycle='0' then 62 | rand(rng_state,0,3,delay); 63 | if delay>0 then 64 | for i in 1 to delay loop 65 | wait until rising_edge(clk_i) and wbm_cyc_i='1' and wbm_stb_i='1'; 66 | end loop; 67 | end if; 68 | end if; 69 | 70 | if ack='0' then 71 | wbm_dat_o<=("00"&wbm_adr_i) xor xor_constant; 72 | ack<='1'; 73 | elsif wbm_cti_i="010" and wbm_bte_i="00" then 74 | wbm_dat_o<=("00"&std_logic_vector(unsigned(wbm_adr_i)+1)) xor xor_constant; 75 | ack<='1'; 76 | end if; 77 | end process; 78 | 79 | end architecture; 80 | -------------------------------------------------------------------------------- /tools/src/wigen/generator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the Generator class which generates 7 | * WISHBONE interconnect VHDL description based on provided 8 | * parameters. 9 | */ 10 | 11 | #ifndef GENERATOR_H_INCLUDED 12 | #define GENERATOR_H_INCLUDED 13 | 14 | #include "range.h" 15 | 16 | #include 17 | #include 18 | 19 | class Generator { 20 | int _masters; 21 | int _slaves; 22 | int _addrWidth; 23 | int _slaveAddrWidth; 24 | int _portSize; 25 | int _portGranularity; 26 | 27 | std::string _entityName; 28 | bool _pipelinedArbiter; 29 | bool _registeredFeedback; 30 | bool _unsafeDecoder; 31 | 32 | Range _mastersRange; 33 | Range _slavesRange; 34 | Range _addrRange; 35 | Range _slaveAddrRange; 36 | Range _slaveDecoderRange; 37 | Range _dataRange; 38 | Range _selRange; 39 | 40 | bool _fallbackSlave; 41 | 42 | public: 43 | Generator(); 44 | 45 | void setMasters(int i); 46 | void setSlaves(int i); 47 | void setAddrWidth(int i); 48 | void setSlaveAddrWidth(int i); 49 | void setPortSize(int i); 50 | void setPortGranularity(int i); 51 | void setEntityName(const std::string &str); 52 | void setPipelinedArbiter(bool b); 53 | void setRegisteredFeedback(bool b); 54 | void setUnsafeDecoder(bool b); 55 | 56 | int masters() const; 57 | int slaves() const; 58 | int addrWidth() const; 59 | int slaveAddrWidth() const; 60 | int portSize() const; 61 | int portGranularity() const; 62 | std::string entityName() const; 63 | bool pipelinedArbiter() const; 64 | bool registeredFeedback() const; 65 | bool unsafeDecoder() const; 66 | 67 | void generate(const std::string &filename); 68 | 69 | private: 70 | void prepare(); 71 | void writeBanner(std::ostream &os); 72 | void writePreamble(std::ostream &os); 73 | void writeEntity(std::ostream &os); 74 | void writeArchitecture(std::ostream &os); 75 | 76 | void writeArbiter(std::ostream &os); 77 | void writeMasterMux(std::ostream &os); 78 | void writeMasterDemux(std::ostream &os); 79 | void writeSlaveMux(std::ostream &os); 80 | void writeSlaveDemux(std::ostream &os); 81 | 82 | static std::string binaryLiteral(int value,int n); 83 | static std::string decodedLiteral(int value,int n); 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /rtl/lxp32_scratchpad.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Scratchpad 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- LXP32 register file implemented as a RAM block. Since we need 9 | -- to read two registers simultaneously, the memory is duplicated. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | 15 | entity lxp32_scratchpad is 16 | port( 17 | clk_i: in std_logic; 18 | 19 | raddr1_i: in std_logic_vector(7 downto 0); 20 | rdata1_o: out std_logic_vector(31 downto 0); 21 | raddr2_i: in std_logic_vector(7 downto 0); 22 | rdata2_o: out std_logic_vector(31 downto 0); 23 | 24 | waddr_i: in std_logic_vector(7 downto 0); 25 | we_i: in std_logic; 26 | wdata_i: in std_logic_vector(31 downto 0) 27 | ); 28 | end entity; 29 | 30 | architecture rtl of lxp32_scratchpad is 31 | 32 | signal wdata_reg: std_logic_vector(wdata_i'range); 33 | signal ram1_rdata: std_logic_vector(31 downto 0); 34 | signal ram2_rdata: std_logic_vector(31 downto 0); 35 | 36 | signal ram1_collision: std_logic; 37 | signal ram2_collision: std_logic; 38 | 39 | begin 40 | 41 | -- RAM 1 42 | 43 | ram_inst1: entity work.lxp32_ram256x32(rtl) 44 | port map( 45 | clk_i=>clk_i, 46 | 47 | we_i=>we_i, 48 | waddr_i=>waddr_i, 49 | wdata_i=>wdata_i, 50 | 51 | re_i=>'1', 52 | raddr_i=>raddr1_i, 53 | rdata_o=>ram1_rdata 54 | ); 55 | 56 | -- RAM 2 57 | 58 | ram_inst2: entity work.lxp32_ram256x32(rtl) 59 | port map( 60 | clk_i=>clk_i, 61 | 62 | we_i=>we_i, 63 | waddr_i=>waddr_i, 64 | wdata_i=>wdata_i, 65 | 66 | re_i=>'1', 67 | raddr_i=>raddr2_i, 68 | rdata_o=>ram2_rdata 69 | ); 70 | 71 | -- Read/write collision detection 72 | 73 | process (clk_i) is 74 | begin 75 | if rising_edge(clk_i) then 76 | wdata_reg<=wdata_i; 77 | if waddr_i=raddr1_i and we_i='1' then 78 | ram1_collision<='1'; 79 | else 80 | ram1_collision<='0'; 81 | end if; 82 | if waddr_i=raddr2_i and we_i='1' then 83 | ram2_collision<='1'; 84 | else 85 | ram2_collision<='0'; 86 | end if; 87 | end if; 88 | end process; 89 | 90 | rdata1_o<=ram1_rdata when ram1_collision='0' else wdata_reg; 91 | rdata2_o<=ram2_rdata when ram2_collision='0' else wdata_reg; 92 | 93 | end architecture; 94 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test009.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies call and ret instructions 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, failure 8 | lc sp, 0x00010000 // stack pointer 9 | 10 | lc r0, 0x00008000 11 | sw r0, 0 12 | 13 | // Test simple procedure call 14 | 15 | lc r1, testproc 16 | 17 | call r1 // testproc 18 | 19 | lw r0, r0 20 | lc r1, 0x11223344 21 | 22 | cjmpne r102, r0, r1 // failure 23 | 24 | // Test jump directly to CALL instruction 25 | lc r1, jump_to_call 26 | lc r2, testproc2 27 | 28 | jmp r1 29 | nop 30 | nop 31 | nop 32 | 33 | jump_to_call: 34 | call r2 35 | 36 | lw r0, r0 37 | lc r1, 0x55667788 38 | 39 | cjmpne r102, r0, r1 // failure 40 | 41 | // Test recursive calls: calculate 10th Fibonnaci number 42 | // using recursive algorithm 43 | mov r0, 10 // argument 44 | mov r16, 0 // how many times test_recursive has been called 45 | lc r1, test_recursive 46 | call r1 // test_recursive 47 | 48 | lc r1, 0x00008000 49 | sw r1, r0 50 | 51 | add r1, r1, 4 52 | sw r1, r16 53 | 54 | lc r1, 55 55 | cjmpne r102, r0, r1 56 | 57 | lc r1, 177 58 | cjmpne r102, r16, r1 59 | 60 | sw r100, 1 61 | jmp r101 // halt 62 | 63 | failure: 64 | sw r100, 2 65 | 66 | halt: 67 | hlt 68 | jmp r101 // halt 69 | 70 | testproc: 71 | lc r0, 0x00008000 72 | lc r1, 0x11223344 73 | sw r0, r1 74 | ret 75 | 76 | testproc2: 77 | lc r0, 0x00008000 78 | lc r1, 0x55667788 79 | sw r0, r1 80 | ret 81 | 82 | test_recursive: 83 | add r16, r16, 1 // increment call counter 84 | 85 | // If r0 is 0 or 1, just return 86 | cjmpe rp, r0, 0 87 | cjmpe rp, r0, 1 88 | 89 | // Save return address in stack 90 | sub sp, sp, 4 91 | sw sp, rp 92 | // Save argument in stack 93 | sub sp, sp, 4 94 | sw sp, r0 95 | // Call itself for with (r0-1) and (r0-2) arguments 96 | sub r0, r0, 1 97 | lc r1, test_recursive 98 | call r1 99 | // Restore value from stack, save temporary result 100 | lw r1, sp 101 | sw sp, r0 102 | 103 | sub r0, r1, 2 104 | lc r1, test_recursive 105 | call r1 106 | 107 | // Restore result from stack 108 | lw r1, sp 109 | add sp, sp, 4 110 | 111 | add r0, r0, r1 112 | 113 | // Restore return address 114 | lw rp, sp 115 | add sp, sp, 4 116 | ret 117 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/linkableobject.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the LinkableObject class which represents 7 | * compiled LXP32 binary code. 8 | */ 9 | 10 | #ifndef LINKABLEOBJECT_H_INCLUDED 11 | #define LINKABLEOBJECT_H_INCLUDED 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class LinkableObject { 20 | public: 21 | typedef unsigned char Byte; 22 | typedef std::uint32_t Word; 23 | typedef std::int_least64_t Integer; 24 | 25 | enum SymbolType {Unknown,Local,Exported,Imported}; 26 | enum RefType {Regular,Short}; 27 | 28 | struct Reference { 29 | std::string source; 30 | int line; 31 | Word rva; 32 | Integer offset; 33 | RefType type; 34 | }; 35 | struct SymbolData { 36 | SymbolType type=Unknown; 37 | Word rva; 38 | std::vector refs; 39 | }; 40 | 41 | typedef std::map SymbolTable; 42 | 43 | private: 44 | std::string _name; 45 | std::vector _code; 46 | SymbolTable _symbols; 47 | Word _virtualAddress=0; 48 | 49 | public: 50 | std::string name() const; 51 | void setName(const std::string &str); 52 | 53 | Word virtualAddress() const; 54 | void setVirtualAddress(Word addr); 55 | 56 | Byte *code(); 57 | const Byte *code() const; 58 | std::size_t codeSize() const; 59 | 60 | Word addWord(Word w); 61 | Word addByte(Byte b); 62 | Word addBytes(const Byte *p,std::size_t n); 63 | Word addZeros(std::size_t n); 64 | 65 | Word addPadding(std::size_t size=sizeof(LinkableObject::Word)); 66 | 67 | Word getWord(Word rva) const; 68 | void replaceWord(Word rva,Word value); 69 | 70 | void addSymbol(const std::string &name,Word rva); 71 | void addImportedSymbol(const std::string &name); 72 | void exportSymbol(const std::string &name); 73 | void addReference(const std::string &symbolName,const Reference &ref); 74 | 75 | SymbolData &symbol(const std::string &name); 76 | const SymbolData &symbol(const std::string &name) const; 77 | const SymbolTable &symbols() const; 78 | 79 | void serialize(const std::string &filename) const; 80 | void deserialize(const std::string &filename); 81 | 82 | private: 83 | void deserializeCode(std::istream &in); 84 | void deserializeSymbol(std::istream &in); 85 | static std::vector tokenize(const std::string &str); 86 | }; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /doc/src/trm/preamble.tex: -------------------------------------------------------------------------------- 1 | \usepackage{microtype} 2 | \usepackage{graphicx} 3 | \usepackage{alltt} 4 | \usepackage{amsmath} 5 | \usepackage[charter]{mathdesign} 6 | 7 | \usepackage{fontspec} 8 | \setmainfont[Ligatures=TeX]{XCharter} 9 | \setmonofont[Scale=MatchLowercase]{DejaVu Sans Mono} 10 | 11 | \usepackage[english]{babel} 12 | 13 | \usepackage[perpage]{footmisc} 14 | 15 | \newcommand{\styledtitleref}[1]{\emph{\titleref{#1}}} 16 | 17 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 18 | % Various Memoir class settings 19 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 20 | 21 | \sloppybottom 22 | \setsecnumdepth{section} 23 | \settocdepth{section} 24 | 25 | \setpnumwidth{2.55em} 26 | \setrmarg{3.55em} 27 | 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | % Code 30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31 | 32 | \newcommand{\shellcmd}[1]{\texttt{#1}} 33 | \newcommand{\code}[1]{\mbox{\texttt{#1}}} 34 | \newcommand{\instr}[1]{\texttt{\textbf{#1}}} 35 | \newcommand{\instrname}[1]{\emph{#1}} 36 | \newcommand{\signal}[1]{\texttt{#1}} 37 | 38 | \newenvironment{codepar}{% 39 | \vspace{0.5\baselineskip}% 40 | \begin{minipage}{0.9\textwidth}\begin{alltt} 41 | }{% 42 | \end{alltt}\end{minipage}% 43 | \vspace{0.5\baselineskip} 44 | } 45 | 46 | \newenvironment{codeparbreakable}{% 47 | \vspace{0.5\baselineskip}% 48 | \begin{alltt} 49 | }{% 50 | \end{alltt}% 51 | \vspace{0.5\baselineskip} 52 | } 53 | 54 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 55 | % Tables 56 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 57 | 58 | \newcolumntype{L}{>{\raggedright\arraybackslash}X} 59 | \newcolumntype{R}{>{\raggedleft\arraybackslash}X} 60 | \newcolumntype{C}{>{\centering\arraybackslash}X} 61 | 62 | \newcolumntype{Q}[1]{>{\raggedright\arraybackslash}m{#1}} 63 | \newcolumntype{E}[1]{>{\raggedleft\arraybackslash}m{#1}} 64 | \newcolumntype{W}[1]{>{\centering\arraybackslash}m{#1}} 65 | 66 | \newcommand{\tabcutin}[2]{\multicolumn{#1}{c}{\emph{#2}}} 67 | 68 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 69 | % Various stuff 70 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 71 | 72 | \newcommand{\cplusplus}{C\texttt{\raisebox{0.05em}{++}}} 73 | \newcommand{\lxp}{\textls[60]{LXP}32} 74 | \newcommand{\tocitem}[2]{\phantomsection\addcontentsline{toc}{#1}{#2}} 75 | 76 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 77 | % Hyperlinks 78 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 79 | 80 | \usepackage[bookmarks=true, 81 | bookmarksnumbered=true, 82 | bookmarksdepth=2, 83 | hypertexnames=false] 84 | {hyperref} 85 | 86 | \hypersetup{ 87 | pdftitle={LXP32 Technical Reference Manual}, 88 | pdfauthor={Alex I. Kuznetsov} 89 | } 90 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test021.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies various interrupt trigger modes 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, failure 8 | lc r103, 0x40000000 // timer: number of pulses (0xFFFFFFFF - infinite) 9 | lc r104, 0x40000004 // timer: delay between pulses (in cycles) 10 | lc r105, 0x40000008 // timer: trigger mode 11 | lc r106, 0x4000000C // timer: clear interrupt 12 | 13 | // Rising edge trigger 14 | mov cr, 0 15 | lc iv3, timer_handler_edge 16 | sw r105, 0 17 | lc cr, 0x00000008 // enable interrupt 18 | 19 | lc r32, 1000 // cycle counter 20 | lc r33, cnt_loop1 21 | mov r34, 0 // interrupt call counter 22 | 23 | sw r104, 100 24 | sw r103, 3 25 | 26 | cnt_loop1: 27 | sub r32, r32, 1 28 | cjmpug r33, r32, 0 // cnt_loop 29 | 30 | cjmpne r102, r34, 3 // failure 31 | 32 | // Falling edge trigger 33 | mov cr, 0 34 | lc iv3, timer_handler_edge 35 | sw r105, 2 36 | lc cr, 0x08000008 // enable interrupt 37 | 38 | lc r32, 1000 // cycle counter 39 | lc r33, cnt_loop2 40 | mov r34, 0 // interrupt call counter 41 | 42 | sw r104, 100 43 | sw r103, 4 44 | 45 | cnt_loop2: 46 | sub r32, r32, 1 47 | cjmpug r33, r32, 0 // cnt_loop 48 | 49 | cjmpne r102, r34, 4 // failure 50 | 51 | // High level trigger 52 | mov cr, 0 53 | lc iv3, timer_handler_level 54 | sw r105, 1 55 | lc cr, 0x00080008 // enable interrupt 56 | 57 | lc r32, 1000 // cycle counter 58 | lc r33, cnt_loop3 59 | mov r34, 0 // interrupt call counter 60 | 61 | sw r104, 100 62 | sw r103, 5 63 | 64 | cnt_loop3: 65 | sub r32, r32, 1 66 | cjmpug r33, r32, 0 // cnt_loop 67 | 68 | cjmpne r102, r34, 5 // failure 69 | 70 | // Low level trigger 71 | mov cr, 0 72 | lc iv3, timer_handler_level 73 | sw r105, 3 74 | lc cr, 0x08080008 // enable interrupt 75 | 76 | lc r32, 1000 // cycle counter 77 | lc r33, cnt_loop4 78 | mov r34, 0 // interrupt call counter 79 | 80 | sw r104, 100 81 | sw r103, 6 82 | 83 | cnt_loop4: 84 | sub r32, r32, 1 85 | cjmpug r33, r32, 0 // cnt_loop 86 | 87 | cjmpne r102, r34, 6 // failure 88 | 89 | 90 | sw r100, 1 91 | jmp r101 // halt 92 | 93 | failure: 94 | sw r100, 2 95 | 96 | halt: 97 | hlt 98 | jmp r101 // halt 99 | 100 | timer_handler_edge: 101 | add r34, r34, 1 102 | lc r0, 0x10000004 103 | sw r0, r34 104 | iret 105 | 106 | timer_handler_level: 107 | add r34, r34, 1 108 | sw r106, 1 109 | lc r0, 0x10000004 110 | sw r0, r34 111 | iret 112 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/coprocessor.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Coprocessor 3 | -- 4 | -- Part of the LXP32 test platform 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Performs a simple arithmetic operation, uses interrupt to wake 9 | -- up the CPU. 10 | -- 11 | -- Note: regardless of whether this description is synthesizable, 12 | -- it was designed exclusively for simulation purposes. 13 | --------------------------------------------------------------------- 14 | 15 | library ieee; 16 | use ieee.std_logic_1164.all; 17 | use ieee.numeric_std.all; 18 | 19 | entity coprocessor is 20 | port( 21 | clk_i: in std_logic; 22 | rst_i: in std_logic; 23 | 24 | wbs_cyc_i: in std_logic; 25 | wbs_stb_i: in std_logic; 26 | wbs_we_i: in std_logic; 27 | wbs_sel_i: in std_logic_vector(3 downto 0); 28 | wbs_ack_o: out std_logic; 29 | wbs_adr_i: in std_logic_vector(27 downto 2); 30 | wbs_dat_i: in std_logic_vector(31 downto 0); 31 | wbs_dat_o: out std_logic_vector(31 downto 0); 32 | 33 | irq_o: out std_logic 34 | ); 35 | end entity; 36 | 37 | architecture rtl of coprocessor is 38 | 39 | signal value: unsigned(31 downto 0):=(others=>'0'); 40 | signal result: unsigned(31 downto 0):=(others=>'0'); 41 | signal cnt: integer range 0 to 50:=0; 42 | signal irq: std_logic:='0'; 43 | 44 | begin 45 | 46 | process (clk_i) is 47 | begin 48 | if rising_edge(clk_i) then 49 | if rst_i='1' then 50 | value<=(others=>'0'); 51 | cnt<=0; 52 | irq<='0'; 53 | else 54 | if cnt>0 then 55 | cnt<=cnt-1; 56 | end if; 57 | 58 | if cnt=1 then 59 | irq<='1'; 60 | else 61 | irq<='0'; 62 | end if; 63 | 64 | if wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' then 65 | for i in wbs_sel_i'range loop 66 | if wbs_sel_i(i)='1' then 67 | if wbs_adr_i="00"&X"000000" then 68 | value(i*8+7 downto i*8)<= 69 | unsigned(wbs_dat_i(i*8+7 downto i*8)); 70 | cnt<=50; 71 | end if; 72 | end if; 73 | end loop; 74 | end if; 75 | end if; 76 | end if; 77 | end process; 78 | 79 | process (clk_i) is 80 | begin 81 | if rising_edge(clk_i) then 82 | if rst_i='1' then 83 | result<=(others=>'0'); 84 | else 85 | result<=shift_left(value,1)+value; 86 | end if; 87 | end if; 88 | end process; 89 | 90 | wbs_ack_o<=wbs_cyc_i and wbs_stb_i; 91 | wbs_dat_o<=std_logic_vector(value) when wbs_adr_i="00"&X"000000" else 92 | std_logic_vector(result) when wbs_adr_i="00"&X"000001" else 93 | (others=>'-'); 94 | 95 | irq_o<=irq; 96 | 97 | end architecture; 98 | -------------------------------------------------------------------------------- /rtl/lxp32u_top.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32U CPU top-level module (U-series, without instruction cache) 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- This version uses a Low Latency Interface for the instruction bus 9 | -- (IBUS). It is designed for low-latency slaves such as on-chip 10 | -- RAM blocks. 11 | -- 12 | -- Parameters: 13 | -- DBUS_RMW: Use RMW cycle instead of SEL_O() signal 14 | -- for byte-granular access to data bus 15 | -- DIVIDER_EN: enable divider 16 | -- MUL_ARCH: multiplier architecture ("dsp", "opt" 17 | -- or "seq") 18 | -- START_ADDR: address in program memory where execution 19 | -- starts 20 | --------------------------------------------------------------------- 21 | 22 | library ieee; 23 | use ieee.std_logic_1164.all; 24 | 25 | entity lxp32u_top is 26 | generic( 27 | DBUS_RMW: boolean:=false; 28 | DIVIDER_EN: boolean:=true; 29 | MUL_ARCH: string:="dsp"; 30 | START_ADDR: std_logic_vector(31 downto 0):=(others=>'0') 31 | ); 32 | port( 33 | clk_i: in std_logic; 34 | rst_i: in std_logic; 35 | 36 | lli_re_o: out std_logic; 37 | lli_adr_o: out std_logic_vector(29 downto 0); 38 | lli_dat_i: in std_logic_vector(31 downto 0); 39 | lli_busy_i: in std_logic; 40 | 41 | dbus_cyc_o: out std_logic; 42 | dbus_stb_o: out std_logic; 43 | dbus_we_o: out std_logic; 44 | dbus_sel_o: out std_logic_vector(3 downto 0); 45 | dbus_ack_i: in std_logic; 46 | dbus_adr_o: out std_logic_vector(31 downto 2); 47 | dbus_dat_o: out std_logic_vector(31 downto 0); 48 | dbus_dat_i: in std_logic_vector(31 downto 0); 49 | 50 | irq_i: in std_logic_vector(7 downto 0) 51 | ); 52 | end entity; 53 | 54 | architecture rtl of lxp32u_top is 55 | 56 | begin 57 | 58 | cpu_inst: entity work.lxp32_cpu(rtl) 59 | generic map( 60 | DBUS_RMW=>DBUS_RMW, 61 | DIVIDER_EN=>DIVIDER_EN, 62 | MUL_ARCH=>MUL_ARCH, 63 | START_ADDR=>START_ADDR 64 | ) 65 | port map( 66 | clk_i=>clk_i, 67 | rst_i=>rst_i, 68 | 69 | lli_re_o=>lli_re_o, 70 | lli_adr_o=>lli_adr_o, 71 | lli_dat_i=>lli_dat_i, 72 | lli_busy_i=>lli_busy_i, 73 | 74 | dbus_cyc_o=>dbus_cyc_o, 75 | dbus_stb_o=>dbus_stb_o, 76 | dbus_we_o=>dbus_we_o, 77 | dbus_sel_o=>dbus_sel_o, 78 | dbus_ack_i=>dbus_ack_i, 79 | dbus_adr_o=>dbus_adr_o, 80 | dbus_dat_o=>dbus_dat_o, 81 | dbus_dat_i=>dbus_dat_i, 82 | 83 | irq_i=>irq_i 84 | ); 85 | 86 | end architecture; 87 | -------------------------------------------------------------------------------- /tools/src/lxp32dump/disassembler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the Disassembler class which disassembles 7 | * LXP32 executable code. 8 | */ 9 | 10 | #ifndef DISASSEMBLER_H_INCLUDED 11 | #define DISASSEMBLER_H_INCLUDED 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class Disassembler { 18 | public: 19 | enum Format {Bin,Textio,Dec,Hex}; 20 | typedef std::uint32_t Word; 21 | private: 22 | class Operand { 23 | public: 24 | enum Type {Register,Direct}; 25 | private: 26 | Type _type; 27 | int _value; 28 | public: 29 | Operand(Type t,int value); 30 | Type type() const; 31 | int value() const; 32 | }; 33 | 34 | std::istream &_is; 35 | std::ostream &_os; 36 | Format _fmt; 37 | bool _preferAliases; 38 | int _lineNumber; 39 | Word _pos; 40 | public: 41 | Disassembler(std::istream &is,std::ostream &os); 42 | void setFormat(Format fmt); 43 | void setBase(Word base); 44 | void setPreferAliases(bool b); 45 | void dump(); 46 | 47 | template static std::string hex(const T &w) { 48 | static_assert(std::is_integral::value,"Argument must be of integral type"); 49 | const char *hexstr="0123456789ABCDEF"; 50 | std::string res; 51 | 52 | res.reserve(sizeof(T)*2); 53 | 54 | for(int i=sizeof(T)*8-4;i>=0;i-=4) { 55 | res.push_back(hexstr[(w>>i)&0x0F]); 56 | } 57 | return res; 58 | } 59 | private: 60 | bool getWord(Word &w); 61 | std::string str(const Operand &op); 62 | static Operand decodeRd1Operand(Word w); 63 | static Operand decodeRd2Operand(Word w); 64 | static Operand decodeDstOperand(Word w); 65 | 66 | std::string decodeSimpleInstruction(const std::string &op,Word w); 67 | std::string decodeAdd(Word w); 68 | std::string decodeAnd(Word w); 69 | std::string decodeCjmpxx(Word w); 70 | std::string decodeDivs(Word w); 71 | std::string decodeDivu(Word w); 72 | std::string decodeHlt(Word w); 73 | std::string decodeJmp(Word w); 74 | std::string decodeLc(Word w,bool &valid,Word &operand); 75 | std::string decodeLcs(Word w); 76 | std::string decodeLsb(Word w); 77 | std::string decodeLub(Word w); 78 | std::string decodeLw(Word w); 79 | std::string decodeMods(Word w); 80 | std::string decodeModu(Word w); 81 | std::string decodeMul(Word w); 82 | std::string decodeNop(Word w); 83 | std::string decodeOr(Word w); 84 | std::string decodeSb(Word w); 85 | std::string decodeSl(Word w); 86 | std::string decodeSrs(Word w); 87 | std::string decodeSru(Word w); 88 | std::string decodeSub(Word w); 89 | std::string decodeSw(Word w); 90 | std::string decodeXcall(Word w); 91 | std::string decodeXor(Word w); 92 | std::string decodeWord(Word w); 93 | }; 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /verify/icache/src/tb/tb.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32 instruction cache verification environment (self-checking 3 | -- testbench) 4 | -- 5 | -- Part of the LXP32 instruction cache testbench 6 | -- 7 | -- Copyright (c) 2016 by Alex I. Kuznetsov 8 | -- 9 | -- Parameters: 10 | -- CACHE_BURST_SIZE: burst size for cache unit 11 | -- CACHE_PREFETCH_SIZE: prefetch distance for cache unit 12 | -- CPU_BLOCKS: number of data blocks to fetch 13 | -- VERBOSE: print more messages 14 | --------------------------------------------------------------------- 15 | 16 | library ieee; 17 | use ieee.std_logic_1164.all; 18 | 19 | entity tb is 20 | generic( 21 | CACHE_BURST_SIZE: integer:=16; 22 | CACHE_PREFETCH_SIZE: integer:=32; 23 | CPU_BLOCKS: integer:=100000; 24 | VERBOSE: boolean:=false 25 | ); 26 | end entity; 27 | 28 | architecture testbench of tb is 29 | 30 | signal clk: std_logic:='0'; 31 | signal rst: std_logic:='0'; 32 | 33 | signal lli_re: std_logic; 34 | signal lli_adr: std_logic_vector(29 downto 0); 35 | signal lli_dat: std_logic_vector(31 downto 0); 36 | signal lli_busy: std_logic; 37 | 38 | signal wbm_cyc: std_logic; 39 | signal wbm_stb: std_logic; 40 | signal wbm_cti: std_logic_vector(2 downto 0); 41 | signal wbm_bte: std_logic_vector(1 downto 0); 42 | signal wbm_ack: std_logic; 43 | signal wbm_adr: std_logic_vector(29 downto 0); 44 | signal wbm_dat: std_logic_vector(31 downto 0); 45 | 46 | signal finish: std_logic:='0'; 47 | 48 | begin 49 | 50 | clk<=not clk and not finish after 5 ns; 51 | 52 | dut: entity work.lxp32_icache(rtl) 53 | generic map( 54 | BURST_SIZE=>CACHE_BURST_SIZE, 55 | PREFETCH_SIZE=>CACHE_PREFETCH_SIZE 56 | ) 57 | port map( 58 | clk_i=>clk, 59 | rst_i=>rst, 60 | 61 | lli_re_i=>lli_re, 62 | lli_adr_i=>lli_adr, 63 | lli_dat_o=>lli_dat, 64 | lli_busy_o=>lli_busy, 65 | 66 | wbm_cyc_o=>wbm_cyc, 67 | wbm_stb_o=>wbm_stb, 68 | wbm_cti_o=>wbm_cti, 69 | wbm_bte_o=>wbm_bte, 70 | wbm_ack_i=>wbm_ack, 71 | wbm_adr_o=>wbm_adr, 72 | wbm_dat_i=>wbm_dat 73 | ); 74 | 75 | ram_model_inst: entity work.ram_model(sim) 76 | port map( 77 | clk_i=>clk, 78 | 79 | wbm_cyc_i=>wbm_cyc, 80 | wbm_stb_i=>wbm_stb, 81 | wbm_cti_i=>wbm_cti, 82 | wbm_bte_i=>wbm_bte, 83 | wbm_ack_o=>wbm_ack, 84 | wbm_adr_i=>wbm_adr, 85 | wbm_dat_o=>wbm_dat 86 | ); 87 | 88 | cpu_model_inst: entity work.cpu_model(sim) 89 | generic map( 90 | BLOCKS=>CPU_BLOCKS, 91 | VERBOSE=>VERBOSE 92 | ) 93 | port map( 94 | clk_i=>clk, 95 | 96 | lli_re_o=>lli_re, 97 | lli_adr_o=>lli_adr, 98 | lli_dat_i=>lli_dat, 99 | lli_busy_i=>lli_busy, 100 | 101 | finish_o=>finish 102 | ); 103 | 104 | end architecture; 105 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/outputwriter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module implements members of the OutputWriter class 7 | * and its derived classes. 8 | */ 9 | 10 | #include "outputwriter.h" 11 | #include "utils.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /* 22 | * OutputWriter members 23 | */ 24 | 25 | void OutputWriter::write(const char *data,std::size_t n) { 26 | writeData(data,n); 27 | _size+=n; 28 | } 29 | 30 | void OutputWriter::pad(std::size_t size) { 31 | static char zeros[256]; // static objects are zero-initialized 32 | while(size>0) { 33 | auto n=std::min(size,256); 34 | write(zeros,n); 35 | size-=n; 36 | } 37 | } 38 | 39 | std::size_t OutputWriter::size() const { 40 | return _size; 41 | } 42 | 43 | /* 44 | * BinaryOutputWriter members 45 | */ 46 | 47 | BinaryOutputWriter::BinaryOutputWriter(const std::string &filename): 48 | _filename(filename), 49 | _os(filename,std::ios_base::out|std::ios_base::binary) 50 | { 51 | if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); 52 | } 53 | 54 | void BinaryOutputWriter::writeData(const char *data,std::size_t n) { 55 | _os.write(data,n); 56 | } 57 | 58 | void BinaryOutputWriter::abort() { 59 | _os.close(); 60 | std::remove(_filename.c_str()); 61 | } 62 | 63 | /* 64 | * TextOutputWriter members 65 | */ 66 | 67 | TextOutputWriter::TextOutputWriter(const std::string &filename,Format f): 68 | _filename(filename), 69 | _os(filename,std::ios_base::out), 70 | _fmt(f) 71 | { 72 | if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); 73 | } 74 | 75 | TextOutputWriter::~TextOutputWriter() { 76 | if(!_buf.empty()) { 77 | assert(_buf.size()<4); 78 | pad(4-_buf.size()); 79 | } 80 | } 81 | 82 | void TextOutputWriter::writeData(const char *data,std::size_t n) { 83 | while(n>0) { 84 | assert(_buf.size()<4); 85 | auto count=std::min(4-_buf.size(),n); 86 | _buf.append(data,count); 87 | data+=count; 88 | n-=count; 89 | 90 | if(_buf.size()<4) continue; 91 | 92 | assert(_buf.size()==4); 93 | 94 | std::uint32_t word=(static_cast(_buf[3])<<24)| 95 | (static_cast(_buf[2])<<16)| 96 | (static_cast(_buf[1])<<8)| 97 | static_cast(_buf[0]); 98 | 99 | if(_fmt==Bin) _os<'-'); 92 | 93 | lli_re_o<=re; 94 | lli_adr_o<=std_logic_vector(adr); 95 | 96 | -- Generate IBUS signals 97 | 98 | ibus_ack_o<=ack; 99 | ibus_dat_o<=lli_dat_i when ack='1' else (others=>'-'); 100 | 101 | end architecture; 102 | -------------------------------------------------------------------------------- /doc/src/trm/frontmatter.tex: -------------------------------------------------------------------------------- 1 | \frontmatter 2 | 3 | % Title page 4 | 5 | \thispagestyle{empty} 6 | \pdfbookmark{Title}{bmk:title} 7 | \calccentering{\unitlength} 8 | \begin{adjustwidth*}{\unitlength}{-\unitlength} 9 | \vspace*{\fill} 10 | \begin{center} 11 | \DoubleSpacing 12 | \includegraphics[scale=0.2]{images/lxp32-logo.pdf}\par 13 | \vspace{\onelineskip} 14 | \huge \lxp{}\par 15 | \Large a lightweight open source 32-bit CPU core\par 16 | \LARGE \textbf{Technical Reference Manual}\par 17 | \vspace{1.2\onelineskip} 18 | \large Version 1.4\par 19 | \vspace*{4\onelineskip} 20 | \end{center} 21 | \vspace*{\fill} 22 | \end{adjustwidth*} 23 | 24 | \clearpage 25 | 26 | % Copyright page 27 | 28 | \thispagestyle{empty} 29 | 30 | { 31 | \small 32 | \setlength{\parindent}{0pt} 33 | \nonzeroparskip 34 | 35 | \vspace*{\fill} 36 | 37 | Copyright \textcopyright{} 2016--2022 by Alex I. Kuznetsov. 38 | 39 | The entire \lxp{} IP core package, including the synthesizable RTL description, verification environment, documentation and software tools, is distributed under the terms of the MIT license reproduced below: 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 46 | 47 | \vspace{4\baselineskip} 48 | 49 | Mentor Graphics and ModelSim are trademarks of Mentor Graphics Corporation. 50 | 51 | Microsemi and IGLOO are trademarks of Microsemi Corporation. 52 | 53 | Microsoft, Windows and Visual Studio are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries. 54 | 55 | Verilog is a registered trademark of Cadence Design Systems, Inc. 56 | 57 | Xilinx, Artix and Vivado are trademarks of Xilinx in the United States and other countries. 58 | 59 | All other trademarks are the property of their respective owners. 60 | } 61 | 62 | \cleardoublepage 63 | 64 | % Table of contents 65 | 66 | \pdfbookmark{\contentsname}{bmk:contents} 67 | 68 | \tableofcontents* 69 | -------------------------------------------------------------------------------- /verify/lxp32/src/tb/tb_pkg_body.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32 testbench package body 3 | -- 4 | -- Part of the LXP32 testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Auxiliary package body for the LXP32 testbench 9 | --------------------------------------------------------------------- 10 | 11 | use std.textio.all; 12 | 13 | library ieee; 14 | use ieee.std_logic_1164.all; 15 | use ieee.numeric_std.all; 16 | 17 | use work.common_pkg.all; 18 | 19 | package body tb_pkg is 20 | procedure load_ram( 21 | filename: string; 22 | signal clk: in std_logic; 23 | signal soc_in: out soc_wbs_in_type; 24 | signal soc_out: in soc_wbs_out_type 25 | ) is 26 | file f: text open read_mode is filename; 27 | variable i: integer:=0; 28 | variable l: line; 29 | variable v: bit_vector(31 downto 0); 30 | begin 31 | wait until rising_edge(clk); 32 | 33 | report "Loading program RAM from """&filename&""""; 34 | 35 | while not endfile(f) loop 36 | readline(f,l); 37 | read(l,v); 38 | 39 | assert i'1'); 45 | soc_in.adr<=std_logic_vector(to_unsigned(i,30)); 46 | soc_in.dat<=to_stdlogicvector(v); 47 | 48 | wait until rising_edge(clk) and soc_out.ack='1'; 49 | 50 | i:=i+1; 51 | end loop; 52 | 53 | report integer'image(i)&" words loaded from """&filename&""""; 54 | 55 | soc_in.cyc<='0'; 56 | soc_in.stb<='0'; 57 | 58 | wait until rising_edge(clk); 59 | end procedure; 60 | 61 | procedure run_test( 62 | filename: string; 63 | signal clk: in std_logic; 64 | signal globals: out soc_globals_type; 65 | signal soc_in: out soc_wbs_in_type; 66 | signal soc_out: in soc_wbs_out_type; 67 | signal result: in monitor_out_type 68 | ) is 69 | begin 70 | -- Assert SoC and CPU resets 71 | wait until rising_edge(clk); 72 | globals.rst_i<='1'; 73 | globals.cpu_rst_i<='1'; 74 | wait until rising_edge(clk); 75 | 76 | -- Deassert SoC reset, leave CPU in reset state for now 77 | globals.rst_i<='0'; 78 | wait until rising_edge(clk); 79 | 80 | -- Load RAM 81 | load_ram(filename,clk,soc_in,soc_out); 82 | 83 | -- Deassert CPU reset 84 | globals.cpu_rst_i<='0'; 85 | 86 | while result.valid/='1' loop 87 | wait until rising_edge(clk); 88 | end loop; 89 | 90 | -- Analyze result 91 | 92 | if result.data=X"00000001" then 93 | report "TEST """&filename&""" RESULT: SUCCESS (return code 0x"& 94 | hex_string(result.data)&")"; 95 | else 96 | report "TEST """&filename&""" RESULT: FAILURE (return code 0x"& 97 | hex_string(result.data)&")" severity failure; 98 | end if; 99 | end procedure; 100 | end package body; 101 | -------------------------------------------------------------------------------- /verify/lxp32/src/firmware/test007.asm: -------------------------------------------------------------------------------- 1 | /* 2 | * This test verifies bitwise shift operations. 3 | */ 4 | 5 | lc r100, 0x10000000 // test result output pointer 6 | lc r101, halt 7 | lc r102, failure 8 | 9 | lc r16, 0x10000004 // output pointer 10 | 11 | // Test left shifts (by comparison with self-addition) 12 | 13 | lc r0, 0x12345678 14 | mov r3, r0 // for comparison 15 | lc r32, sl_loop 16 | mov r1, 0 // counter 17 | 18 | sl_loop: 19 | sl r2, r0, r1 20 | sw r16, r2 21 | cjmpne r102, r2, r3 22 | add r1, r1, 1 23 | add r3, r3, r3 24 | cjmpul r32, r1, 32 25 | 26 | // Test unsigned right shifts (by comparison with pre-calculated values) 27 | 28 | lc r32, sru_loop 29 | lc r17, sru_expected_data 30 | mov r1, 0 // counter 31 | 32 | sru_loop: 33 | sru r2, r0, r1 34 | sw r16, r2 35 | lw r3, r17 36 | cjmpne r102, r2, r3 37 | add r1, r1, 1 38 | add r17, r17, 4 39 | cjmpul r32, r1, 32 40 | 41 | // Test signed right shifts (by comparison with pre-calculated values) 42 | 43 | lc r0, 0x87654321 44 | lc r32, srs_loop 45 | lc r17, srs_expected_data 46 | mov r1, 0 // counter 47 | 48 | srs_loop: 49 | srs r2, r0, r1 50 | sw r16, r2 51 | lw r3, r17 52 | cjmpne r102, r2, r3 53 | add r1, r1, 1 54 | add r17, r17, 4 55 | cjmpul r32, r1, 32 56 | 57 | // Report success 58 | sw r100, 1 59 | jmp r101 60 | 61 | failure: 62 | sw r100, 2 63 | 64 | halt: 65 | hlt 66 | jmp r101 67 | 68 | sru_expected_data: 69 | .word 0x12345678 70 | .word 0x091A2B3C 71 | .word 0x048D159E 72 | .word 0x02468ACF 73 | .word 0x01234567 74 | .word 0x0091A2B3 75 | .word 0x0048D159 76 | .word 0x002468AC 77 | .word 0x00123456 78 | .word 0x00091A2B 79 | .word 0x00048D15 80 | .word 0x0002468A 81 | .word 0x00012345 82 | .word 0x000091A2 83 | .word 0x000048D1 84 | .word 0x00002468 85 | .word 0x00001234 86 | .word 0x0000091A 87 | .word 0x0000048D 88 | .word 0x00000246 89 | .word 0x00000123 90 | .word 0x00000091 91 | .word 0x00000048 92 | .word 0x00000024 93 | .word 0x00000012 94 | .word 0x00000009 95 | .word 0x00000004 96 | .word 0x00000002 97 | .word 0x00000001 98 | .word 0x00000000 99 | .word 0x00000000 100 | .word 0x00000000 101 | 102 | srs_expected_data: 103 | .word 0x87654321 104 | .word 0xC3B2A190 105 | .word 0xE1D950C8 106 | .word 0xF0ECA864 107 | .word 0xF8765432 108 | .word 0xFC3B2A19 109 | .word 0xFE1D950C 110 | .word 0xFF0ECA86 111 | .word 0xFF876543 112 | .word 0xFFC3B2A1 113 | .word 0xFFE1D950 114 | .word 0xFFF0ECA8 115 | .word 0xFFF87654 116 | .word 0xFFFC3B2A 117 | .word 0xFFFE1D95 118 | .word 0xFFFF0ECA 119 | .word 0xFFFF8765 120 | .word 0xFFFFC3B2 121 | .word 0xFFFFE1D9 122 | .word 0xFFFFF0EC 123 | .word 0xFFFFF876 124 | .word 0xFFFFFC3B 125 | .word 0xFFFFFE1D 126 | .word 0xFFFFFF0E 127 | .word 0xFFFFFF87 128 | .word 0xFFFFFFC3 129 | .word 0xFFFFFFE1 130 | .word 0xFFFFFFF0 131 | .word 0xFFFFFFF8 132 | .word 0xFFFFFFFC 133 | .word 0xFFFFFFFE 134 | .word 0xFFFFFFFF 135 | -------------------------------------------------------------------------------- /rtl/lxp32_shifter.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Barrel shifter 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Performs logical (unsigned) and arithmetic (signed) shifts 9 | -- in both directions. Pipeline latency: 1 cycle. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | 15 | entity lxp32_shifter is 16 | port( 17 | clk_i: in std_logic; 18 | rst_i: in std_logic; 19 | ce_i: in std_logic; 20 | d_i: in std_logic_vector(31 downto 0); 21 | s_i: in std_logic_vector(4 downto 0); 22 | right_i: in std_logic; 23 | sig_i: in std_logic; 24 | ce_o: out std_logic; 25 | d_o: out std_logic_vector(31 downto 0) 26 | ); 27 | end entity; 28 | 29 | architecture rtl of lxp32_shifter is 30 | 31 | signal data: std_logic_vector(d_i'range); 32 | signal data_shifted: std_logic_vector(d_i'range); 33 | 34 | signal fill: std_logic; -- 0 for unsigned shifts, sign bit for signed ones 35 | signal fill_v: std_logic_vector(3 downto 0); 36 | 37 | type cascades_type is array (4 downto 0) of std_logic_vector(d_i'range); 38 | signal cascades: cascades_type; 39 | 40 | signal stage2_data: std_logic_vector(d_i'range); 41 | signal stage2_s: std_logic_vector(s_i'range); 42 | signal stage2_fill: std_logic; 43 | signal stage2_fill_v: std_logic_vector(15 downto 0); 44 | signal stage2_right: std_logic; 45 | 46 | signal ceo: std_logic:='0'; 47 | 48 | begin 49 | 50 | -- Internally, data are shifted in left direction. For right shifts 51 | -- we reverse the argument's bit order 52 | 53 | data_gen: for i in data'range generate 54 | data(i)<=d_i(i) when right_i='0' else d_i(d_i'high-i); 55 | end generate; 56 | 57 | -- A set of cascaded shifters shifting by powers of two 58 | 59 | fill<=sig_i and data(0); 60 | fill_v<=(others=>fill); 61 | 62 | cascades(0)<=data(30 downto 0)&fill_v(0) when s_i(0)='1' else data; 63 | cascades(1)<=cascades(0)(29 downto 0)&fill_v(1 downto 0) when s_i(1)='1' else cascades(0); 64 | cascades(2)<=cascades(1)(27 downto 0)&fill_v(3 downto 0) when s_i(2)='1' else cascades(1); 65 | 66 | process (clk_i) is 67 | begin 68 | if rising_edge(clk_i) then 69 | if rst_i='1' then 70 | ceo<='0'; 71 | stage2_data<=(others=>'-'); 72 | stage2_s<=(others=>'-'); 73 | stage2_fill<='-'; 74 | stage2_right<='-'; 75 | else 76 | ceo<=ce_i; 77 | stage2_data<=cascades(2); 78 | stage2_s<=s_i; 79 | stage2_fill<=fill; 80 | stage2_right<=right_i; 81 | end if; 82 | end if; 83 | end process; 84 | 85 | stage2_fill_v<=(others=>stage2_fill); 86 | 87 | cascades(3)<=stage2_data(23 downto 0)&stage2_fill_v(7 downto 0) when stage2_s(3)='1' else stage2_data; 88 | cascades(4)<=cascades(3)(15 downto 0)&stage2_fill_v(15 downto 0) when stage2_s(4)='1' else cascades(3); 89 | 90 | -- Reverse bit order back, if needed 91 | 92 | data_shifted_gen: for i in data_shifted'range generate 93 | data_shifted(i)<=cascades(4)(i) when stage2_right='0' else cascades(4)(cascades(4)'high-i); 94 | end generate; 95 | 96 | d_o<=data_shifted; 97 | ce_o<=ceo; 98 | 99 | end architecture; 100 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/dbus_monitor.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- DBUS monitor 3 | -- 4 | -- Part of the LXP32 test platform 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Monitors LXP32 data bus transactions, optionally throttles them. 9 | -- 10 | -- Note: regardless of whether this description is synthesizable, 11 | -- it was designed exclusively for simulation purposes. 12 | --------------------------------------------------------------------- 13 | 14 | library ieee; 15 | use ieee.std_logic_1164.all; 16 | 17 | entity dbus_monitor is 18 | generic( 19 | THROTTLE: boolean 20 | ); 21 | port( 22 | clk_i: in std_logic; 23 | rst_i: in std_logic; 24 | 25 | wbs_cyc_i: in std_logic; 26 | wbs_stb_i: in std_logic; 27 | wbs_we_i: in std_logic; 28 | wbs_sel_i: in std_logic_vector(3 downto 0); 29 | wbs_ack_o: out std_logic; 30 | wbs_adr_i: in std_logic_vector(31 downto 2); 31 | wbs_dat_i: in std_logic_vector(31 downto 0); 32 | wbs_dat_o: out std_logic_vector(31 downto 0); 33 | 34 | wbm_cyc_o: out std_logic; 35 | wbm_stb_o: out std_logic; 36 | wbm_we_o: out std_logic; 37 | wbm_sel_o: out std_logic_vector(3 downto 0); 38 | wbm_ack_i: in std_logic; 39 | wbm_adr_o: out std_logic_vector(31 downto 2); 40 | wbm_dat_o: out std_logic_vector(31 downto 0); 41 | wbm_dat_i: in std_logic_vector(31 downto 0) 42 | ); 43 | end entity; 44 | 45 | architecture rtl of dbus_monitor is 46 | 47 | signal prbs: std_logic; 48 | signal cycle: std_logic:='0'; 49 | 50 | signal cyc_ff: std_logic:='0'; 51 | signal ack_ff: std_logic:='0'; 52 | 53 | begin 54 | 55 | -- Manage throttling 56 | 57 | gen_throttling: if THROTTLE generate 58 | throttle_inst: entity work.scrambler(rtl) 59 | generic map(TAP1=>6,TAP2=>7) 60 | port map(clk_i=>clk_i,rst_i=>rst_i,ce_i=>'1',d_o=>prbs); 61 | end generate; 62 | 63 | gen_no_throttling: if not THROTTLE generate 64 | prbs<='0'; 65 | end generate; 66 | 67 | -- CPU interface 68 | 69 | wbs_ack_o<=wbm_ack_i; 70 | wbs_dat_o<=wbm_dat_i when wbm_ack_i='1' else (others=>'-'); 71 | 72 | -- Interconnect interface 73 | 74 | process (clk_i) is 75 | begin 76 | if rising_edge(clk_i) then 77 | if rst_i='1' then 78 | cycle<='0'; 79 | elsif prbs='0' and wbs_cyc_i='1' then 80 | cycle<='1'; 81 | elsif wbs_cyc_i='0' then 82 | cycle<='0'; 83 | end if; 84 | end if; 85 | end process; 86 | 87 | wbm_cyc_o<=wbs_cyc_i and (not prbs or cycle); 88 | wbm_stb_o<=wbs_stb_i and (not prbs or cycle); 89 | wbm_we_o<=wbs_we_i; 90 | wbm_sel_o<=wbs_sel_i; 91 | wbm_adr_o<=wbs_adr_i; 92 | wbm_dat_o<=wbs_dat_i; 93 | 94 | -- Check handshake correctness 95 | 96 | process (clk_i) is 97 | begin 98 | if rising_edge(clk_i) then 99 | if rst_i='1' then 100 | cyc_ff<='0'; 101 | ack_ff<='0'; 102 | else 103 | cyc_ff<=wbs_cyc_i; 104 | ack_ff<=wbm_ack_i; 105 | 106 | assert wbm_ack_i='0' or (wbs_cyc_i and (not prbs or cycle))='1' 107 | report "DBUS error: ACK asserted without CYC" 108 | severity failure; 109 | 110 | assert not (wbs_cyc_i='0' and cyc_ff='1' and ack_ff/='1') 111 | report "DBUS error: cycle terminated prematurely" 112 | severity failure; 113 | end if; 114 | end if; 115 | end process; 116 | 117 | end architecture; 118 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/timer.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Timer 3 | -- 4 | -- Part of the LXP32 test platform 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- A simple programmable interval timer. 9 | -- 10 | -- Note: regardless of whether this description is synthesizable, 11 | -- it was designed exclusively for simulation purposes. 12 | --------------------------------------------------------------------- 13 | 14 | library ieee; 15 | use ieee.std_logic_1164.all; 16 | use ieee.numeric_std.all; 17 | 18 | entity timer is 19 | port( 20 | clk_i: in std_logic; 21 | rst_i: in std_logic; 22 | 23 | wbs_cyc_i: in std_logic; 24 | wbs_stb_i: in std_logic; 25 | wbs_we_i: in std_logic; 26 | wbs_sel_i: in std_logic_vector(3 downto 0); 27 | wbs_ack_o: out std_logic; 28 | wbs_adr_i: in std_logic_vector(27 downto 2); 29 | wbs_dat_i: in std_logic_vector(31 downto 0); 30 | wbs_dat_o: out std_logic_vector(31 downto 0); 31 | 32 | elapsed_o: out std_logic 33 | ); 34 | end entity; 35 | 36 | architecture rtl of timer is 37 | 38 | signal irq_level_triggered: std_logic:='0'; 39 | signal irq_invert: std_logic:='0'; 40 | 41 | signal pulses: unsigned(31 downto 0):=(others=>'0'); 42 | signal interval: unsigned(31 downto 0):=(others=>'0'); 43 | signal cnt: unsigned(31 downto 0):=(others=>'0'); 44 | signal elapsed: std_logic:='0'; 45 | 46 | begin 47 | 48 | process (clk_i) is 49 | begin 50 | if rising_edge(clk_i) then 51 | if rst_i='1' then 52 | pulses<=(others=>'0'); 53 | interval<=(others=>'0'); 54 | cnt<=(others=>'0'); 55 | elapsed<='0'; 56 | else 57 | if irq_level_triggered='0' then 58 | elapsed<='0'; 59 | end if; 60 | if pulses/=X"00000000" or cnt/=X"00000000" then 61 | if cnt=X"00000000" then 62 | if pulses/=X"FFFFFFFF" then 63 | pulses<=pulses-1; 64 | end if; 65 | if pulses/=X"00000000" then 66 | cnt<=interval; 67 | end if; 68 | else 69 | cnt<=cnt-1; 70 | end if; 71 | if cnt=X"00000001" then 72 | elapsed<='1'; 73 | end if; 74 | end if; 75 | 76 | if wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' then 77 | for i in wbs_sel_i'range loop 78 | if wbs_sel_i(i)='1' then 79 | if wbs_adr_i="00"&X"000000" then 80 | pulses(i*8+7 downto i*8)<= 81 | unsigned(wbs_dat_i(i*8+7 downto i*8)); 82 | cnt<=(others=>'0'); 83 | end if; 84 | if wbs_adr_i="00"&X"000001" then 85 | interval(i*8+7 downto i*8)<= 86 | unsigned(wbs_dat_i(i*8+7 downto i*8)); 87 | cnt<=(others=>'0'); 88 | end if; 89 | if wbs_adr_i="00"&X"000002" and i=0 then 90 | irq_level_triggered<=wbs_dat_i(0); 91 | irq_invert<=wbs_dat_i(1); 92 | end if; 93 | if wbs_adr_i="00"&X"000003" and wbs_dat_i(0)='1' and i=0 then 94 | elapsed<='0'; 95 | end if; 96 | end if; 97 | end loop; 98 | end if; 99 | end if; 100 | end if; 101 | end process; 102 | 103 | wbs_ack_o<=wbs_cyc_i and wbs_stb_i; 104 | wbs_dat_o<=std_logic_vector(pulses) when wbs_adr_i="00"&X"000000" else 105 | std_logic_vector(interval) when wbs_adr_i="00"&X"000001" else 106 | (others=>'-'); 107 | 108 | elapsed_o<=elapsed xor irq_invert; 109 | 110 | end architecture; 111 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module implements members of the Utils namespace. 7 | */ 8 | 9 | #include "utils.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | std::string Utils::urlEncode(const std::string &str) { 18 | std::string res; 19 | for(std::size_t i=0;i='A'&&ch<='Z') res.push_back(ch); 22 | else if(ch>='a'&&ch<='z') res.push_back(ch); 23 | else if(ch>='0'&&ch<='9') res.push_back(ch); 24 | else if(ch=='-'||ch=='_'||ch=='.'||ch=='~') res.push_back(ch); 25 | else res+="%"+hex(ch); 26 | } 27 | return res; 28 | } 29 | 30 | std::string Utils::urlDecode(const std::string &str) { 31 | std::string res; 32 | for(std::size_t i=0;i(std::stoul(hexcode,nullptr,16)); 41 | res.push_back(static_cast(u)); 42 | } 43 | catch(std::exception &) { 44 | throw std::runtime_error("Ill-formed URL-encoded string"); 45 | } 46 | } 47 | } 48 | return res; 49 | } 50 | 51 | std::string Utils::normalizeSeparators(const std::string &path) { 52 | std::string str(path); 53 | #ifdef _WIN32 54 | std::replace(str.begin(),str.end(),'\\','/'); 55 | #endif 56 | return str; 57 | } 58 | 59 | std::string Utils::nativeSeparators(const std::string &path) { 60 | std::string str(path); 61 | #ifdef _WIN32 62 | std::replace(str.begin(),str.end(),'/','\\'); 63 | #endif 64 | return str; 65 | } 66 | 67 | bool Utils::isAbsolutePath(const std::string &path) { 68 | auto native=nativeSeparators(path); 69 | if(native.empty()) return false; 70 | if(native[0]=='/') return true; 71 | #ifdef _WIN32 72 | if(native.size()>1&&native[1]==':') return true; 73 | #endif 74 | return false; 75 | } 76 | 77 | bool Utils::fileExists(const std::string &path) { 78 | std::ifstream in(nativeSeparators(path),std::ios_base::in); 79 | if(!in) return false; 80 | return true; 81 | } 82 | 83 | std::string Utils::relativePath(const std::string &from,const std::string &to) { 84 | // Normalize directory separators 85 | auto nfrom=normalizeSeparators(from); 86 | auto nto=normalizeSeparators(to); 87 | 88 | if(nto.empty()) return std::string(); 89 | 90 | // If "nto" is an absolute path, just return it 91 | if(isAbsolutePath(nto)) return nativeSeparators(nto); 92 | 93 | // Process relative path 94 | auto pos=nfrom.find_last_of('/'); 95 | if(pos==std::string::npos) return nativeSeparators(nto); 96 | else return nativeSeparators(nfrom.substr(0,pos+1)+nto); 97 | } 98 | 99 | std::string Utils::dequoteString(const std::string &str) { 100 | if(str.size()<2) throw std::runtime_error("String literal expected"); 101 | if(str.front()!='\"'||str.back()!='\"') throw std::runtime_error("String literal expected"); 102 | return str.substr(1,str.size()-2); 103 | } 104 | 105 | bool Utils::ishexdigit(char ch) { 106 | static const char *digits="0123456789ABCDEFabcdef"; 107 | return (std::strchr(digits,ch)!=NULL); 108 | } 109 | 110 | bool Utils::isoctdigit(char ch) { 111 | static const char *digits="01234567"; 112 | return (std::strchr(digits,ch)!=NULL); 113 | } 114 | -------------------------------------------------------------------------------- /rtl/lxp32c_top.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32C CPU top-level module (C-series, with instruction cache) 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- This version uses Wishbone B3 interface for the instruction bus 9 | -- (IBUS). It is designed for high-latency program memory, such as 10 | -- external SDRAM chips. 11 | -- 12 | -- Parameters: 13 | -- DBUS_RMW: Use RMW cycle instead of SEL_O() signal 14 | -- for byte-granular access to data bus 15 | -- DIVIDER_EN: enable divider 16 | -- IBUS_BURST_SIZE: size of the burst 17 | -- IBUS_PREFETCH_SIZE: initiate read burst if number of words 18 | -- left in the buffer is less than specified 19 | -- MUL_ARCH: multiplier architecture ("dsp", "opt" 20 | -- or "seq") 21 | -- START_ADDR: address in program memory where execution 22 | -- starts 23 | --------------------------------------------------------------------- 24 | 25 | library ieee; 26 | use ieee.std_logic_1164.all; 27 | 28 | entity lxp32c_top is 29 | generic( 30 | DBUS_RMW: boolean:=false; 31 | DIVIDER_EN: boolean:=true; 32 | IBUS_BURST_SIZE: integer:=16; 33 | IBUS_PREFETCH_SIZE: integer:=32; 34 | MUL_ARCH: string:="dsp"; 35 | START_ADDR: std_logic_vector(31 downto 0):=(others=>'0') 36 | ); 37 | port( 38 | clk_i: in std_logic; 39 | rst_i: in std_logic; 40 | 41 | ibus_cyc_o: out std_logic; 42 | ibus_stb_o: out std_logic; 43 | ibus_cti_o: out std_logic_vector(2 downto 0); 44 | ibus_bte_o: out std_logic_vector(1 downto 0); 45 | ibus_ack_i: in std_logic; 46 | ibus_adr_o: out std_logic_vector(29 downto 0); 47 | ibus_dat_i: in std_logic_vector(31 downto 0); 48 | 49 | dbus_cyc_o: out std_logic; 50 | dbus_stb_o: out std_logic; 51 | dbus_we_o: out std_logic; 52 | dbus_sel_o: out std_logic_vector(3 downto 0); 53 | dbus_ack_i: in std_logic; 54 | dbus_adr_o: out std_logic_vector(31 downto 2); 55 | dbus_dat_o: out std_logic_vector(31 downto 0); 56 | dbus_dat_i: in std_logic_vector(31 downto 0); 57 | 58 | irq_i: in std_logic_vector(7 downto 0) 59 | ); 60 | end entity; 61 | 62 | architecture rtl of lxp32c_top is 63 | 64 | signal lli_re: std_logic; 65 | signal lli_adr: std_logic_vector(29 downto 0); 66 | signal lli_dat: std_logic_vector(31 downto 0); 67 | signal lli_busy: std_logic; 68 | 69 | begin 70 | 71 | cpu_inst: entity work.lxp32_cpu(rtl) 72 | generic map( 73 | DBUS_RMW=>DBUS_RMW, 74 | DIVIDER_EN=>DIVIDER_EN, 75 | MUL_ARCH=>MUL_ARCH, 76 | START_ADDR=>START_ADDR 77 | ) 78 | port map( 79 | clk_i=>clk_i, 80 | rst_i=>rst_i, 81 | 82 | lli_re_o=>lli_re, 83 | lli_adr_o=>lli_adr, 84 | lli_dat_i=>lli_dat, 85 | lli_busy_i=>lli_busy, 86 | 87 | dbus_cyc_o=>dbus_cyc_o, 88 | dbus_stb_o=>dbus_stb_o, 89 | dbus_we_o=>dbus_we_o, 90 | dbus_sel_o=>dbus_sel_o, 91 | dbus_ack_i=>dbus_ack_i, 92 | dbus_adr_o=>dbus_adr_o, 93 | dbus_dat_o=>dbus_dat_o, 94 | dbus_dat_i=>dbus_dat_i, 95 | 96 | irq_i=>irq_i 97 | ); 98 | 99 | icache_inst: entity work.lxp32_icache(rtl) 100 | generic map( 101 | BURST_SIZE=>IBUS_BURST_SIZE, 102 | PREFETCH_SIZE=>IBUS_PREFETCH_SIZE 103 | ) 104 | port map( 105 | clk_i=>clk_i, 106 | rst_i=>rst_i, 107 | 108 | lli_re_i=>lli_re, 109 | lli_adr_i=>lli_adr, 110 | lli_dat_o=>lli_dat, 111 | lli_busy_o=>lli_busy, 112 | 113 | wbm_cyc_o=>ibus_cyc_o, 114 | wbm_stb_o=>ibus_stb_o, 115 | wbm_cti_o=>ibus_cti_o, 116 | wbm_bte_o=>ibus_bte_o, 117 | wbm_ack_i=>ibus_ack_i, 118 | wbm_adr_o=>ibus_adr_o, 119 | wbm_dat_i=>ibus_dat_i 120 | ); 121 | 122 | end architecture; 123 | -------------------------------------------------------------------------------- /tools/src/lxp32asm/assembler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * This module defines the Assembler class which performs 7 | * compilation of LXP32 assembly source files. 8 | */ 9 | 10 | #ifndef ASSEMBLER_H_INCLUDED 11 | #define ASSEMBLER_H_INCLUDED 12 | 13 | #include "linkableobject.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class Assembler { 21 | typedef std::vector TokenList; 22 | typedef std::int_least64_t Integer; 23 | enum LexerState { 24 | Initial, 25 | Word, 26 | StringLiteral, 27 | BlockComment 28 | }; 29 | struct Operand { 30 | enum Type {Null,Register,Identifier,NumericLiteral}; 31 | Type type=Null; 32 | std::string str; 33 | Integer i=0; 34 | std::uint8_t reg=0; 35 | }; 36 | 37 | LinkableObject _obj; 38 | std::map _macros; 39 | LexerState _state; 40 | int _line; 41 | std::vector _currentLabels; 42 | std::string _currentFileName; 43 | std::vector _includeSearchDirs; 44 | std::vector _exportedSymbols; 45 | std::vector _sectionEnabled; 46 | public: 47 | void processFile(const std::string &filename); 48 | 49 | void addIncludeSearchDir(const std::string &dir); 50 | 51 | int line() const; 52 | std::string currentFileName() const; 53 | 54 | LinkableObject &object(); 55 | const LinkableObject &object() const; 56 | private: 57 | void processFileRecursive(const std::string &filename); 58 | TokenList tokenize(const std::string &str); 59 | void expand(TokenList &list); 60 | void elaborate(TokenList &list); 61 | 62 | void elaborateDirective(TokenList &list); 63 | LinkableObject::Word elaborateDataDefinition(TokenList &list); 64 | LinkableObject::Word elaborateInstruction(TokenList &list); 65 | 66 | bool isSectionEnabled() const; 67 | static bool validateIdentifier(const std::string &str); 68 | static Integer numericLiteral(const std::string &str); 69 | static std::vector getOperands(const TokenList &list); 70 | 71 | // LXP32 instructions 72 | void encodeDstOperand(LinkableObject::Word &word,const Operand &arg); 73 | void encodeRd1Operand(LinkableObject::Word &word,const Operand &arg); 74 | void encodeRd2Operand(LinkableObject::Word &word,const Operand &arg); 75 | 76 | void encodeAdd(const TokenList &list); 77 | void encodeAnd(const TokenList &list); 78 | void encodeCall(const TokenList &list); 79 | void encodeCjmpxx(const TokenList &list); 80 | void encodeDivs(const TokenList &list); 81 | void encodeDivu(const TokenList &list); 82 | void encodeHlt(const TokenList &list); 83 | void encodeJmp(const TokenList &list); 84 | void encodeIret(const TokenList &list); 85 | void encodeLc(const TokenList &list); 86 | void encodeLcs(const TokenList &list); 87 | void encodeLsb(const TokenList &list); 88 | void encodeLub(const TokenList &list); 89 | void encodeLw(const TokenList &list); 90 | void encodeMods(const TokenList &list); 91 | void encodeModu(const TokenList &list); 92 | void encodeMov(const TokenList &list); 93 | void encodeMul(const TokenList &list); 94 | void encodeNeg(const TokenList &list); 95 | void encodeNop(const TokenList &list); 96 | void encodeNot(const TokenList &list); 97 | void encodeOr(const TokenList &list); 98 | void encodeRet(const TokenList &list); 99 | void encodeSb(const TokenList &list); 100 | void encodeSl(const TokenList &list); 101 | void encodeSrs(const TokenList &list); 102 | void encodeSru(const TokenList &list); 103 | void encodeSub(const TokenList &list); 104 | void encodeSw(const TokenList &list); 105 | void encodeXcall(const TokenList &list); 106 | void encodeXor(const TokenList &list); 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /tools/src/wigen/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * Main translation unit for the WISHBONE interconnect generator. 7 | */ 8 | 9 | #include "generator.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | static void displayUsage(std::ostream &os,const char *program) { 18 | os< [ ]"< Number of masters"< Number of slaves"< Master address width"< Slave address width"< Port size"< Port granularity, default: port size"< Entity name, default: \"intercon\""< Output file name, default: \".vhd\""<5) throw std::runtime_error("Too many arguments"); 84 | 85 | int value; 86 | 87 | try { 88 | value=std::stoi(argv[i],nullptr,0); 89 | } 90 | catch(std::exception &) { 91 | throw std::runtime_error("Invalid value"); 92 | } 93 | 94 | switch(mainArg) { 95 | case 0: 96 | gen.setMasters(value); 97 | break; 98 | case 1: 99 | gen.setSlaves(value); 100 | break; 101 | case 2: 102 | gen.setAddrWidth(value); 103 | break; 104 | case 3: 105 | gen.setSlaveAddrWidth(value); 106 | break; 107 | case 4: 108 | gen.setPortSize(value); 109 | break; 110 | case 5: 111 | gen.setPortGranularity(value); 112 | break; 113 | } 114 | mainArg++; 115 | } 116 | } 117 | 118 | if(mainArg<5) throw std::runtime_error("Too few arguments"); 119 | if(mainArg==5) gen.setPortGranularity(gen.portSize()); 120 | 121 | if(outputFileName.empty()) outputFileName=gen.entityName()+".vhd"; 122 | 123 | gen.generate(outputFileName); 124 | } 125 | catch(std::exception &ex) { 126 | std::cerr<<"Error: "<8, 67 | ADDR_WIDTH=>14, 68 | SIZE=>16384, 69 | MODE=>"DONTCARE" 70 | ) 71 | port map( 72 | clka_i=>clk_i, 73 | cea_i=>'1', 74 | wea_i=>ram_a_we(i), 75 | addra_i=>wbs_adr_i(15 downto 2), 76 | da_i=>wbs_dat_i(i*8+7 downto i*8), 77 | da_o=>ram_a_rdata(i*8+7 downto i*8), 78 | 79 | clkb_i=>clk_i, 80 | ceb_i=>ram_b_re, 81 | addrb_i=>lli_adr_i(13 downto 0), 82 | db_o=>ram_b_rdata(i*8+7 downto i*8) 83 | ); 84 | end generate; 85 | 86 | -- WISHBONE interface 87 | 88 | gen_ram_a_we: for i in 3 downto 0 generate 89 | ram_a_we(i)<='1' when wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' 90 | and wbs_sel_i(i)='1' and wbs_adr_i(27 downto 16)="000000000000" else '0'; 91 | end generate; 92 | 93 | process (clk_i) is 94 | begin 95 | if rising_edge(clk_i) then 96 | ack_read<=wbs_cyc_i and wbs_stb_i and not wbs_we_i and not ack_read; 97 | end if; 98 | end process; 99 | 100 | ack_write<=wbs_cyc_i and wbs_stb_i and wbs_we_i; 101 | 102 | wbs_ack_o<=ack_read or ack_write; 103 | wbs_dat_o<=ram_a_rdata; 104 | 105 | -- Low Latency Interface (with optional pseudo-random throttling) 106 | 107 | process (clk_i) is 108 | begin 109 | if rising_edge(clk_i) then 110 | assert lli_re_i='0' or lli_adr_i(lli_adr_i'high downto 14)=X"0000" 111 | report "Attempted to fetch instruction from a non-existent address 0x"& 112 | hex_string(lli_adr_i&"00") 113 | severity failure; 114 | end if; 115 | end process; 116 | 117 | gen_throttling: if THROTTLE generate 118 | throttle_inst: entity work.scrambler(rtl) 119 | generic map(TAP1=>9,TAP2=>11) 120 | port map(clk_i=>clk_i,rst_i=>rst_i,ce_i=>'1',d_o=>prbs); 121 | end generate; 122 | 123 | gen_no_throttling: if not THROTTLE generate 124 | prbs<='0'; 125 | end generate; 126 | 127 | process (clk_i) is 128 | begin 129 | if rising_edge(clk_i) then 130 | if rst_i='1' then 131 | lli_busy<='0'; 132 | elsif prbs='1' and lli_re_i='1' then 133 | lli_busy<='1'; 134 | elsif prbs='0' then 135 | lli_busy<='0'; 136 | end if; 137 | end if; 138 | end process; 139 | 140 | ram_b_re<=lli_re_i and not lli_busy; 141 | 142 | lli_busy_o<=lli_busy; 143 | lli_dat_o<=ram_b_rdata when lli_busy='0' else (others=>'-'); 144 | 145 | end architecture; 146 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/sync_fifo.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Synchronous FIFO 3 | -- 4 | -- Copyright (c) 2015 by Alex I. Kuznetsov 5 | -- 6 | -- Portable description of a synchronous FIFO block. 7 | -- 8 | -- Parameters: 9 | -- * DATA_WIDTH: data port width 10 | -- * ADDR_WIDTH: internal address port width 11 | -- * SIZE: FIFO size, must be <= 2^ADDR_WIDTH 12 | -- * FWFT: true: first word fall-through mode 13 | -- (output is produced without waiting for re_i) 14 | -- false: normal mode 15 | --------------------------------------------------------------------- 16 | 17 | library ieee; 18 | use ieee.std_logic_1164.all; 19 | use ieee.numeric_std.all; 20 | 21 | entity sync_fifo is 22 | generic( 23 | DATA_WIDTH: integer; 24 | ADDR_WIDTH: integer; 25 | SIZE: integer; 26 | FWFT: boolean 27 | ); 28 | port( 29 | clk_i: in std_logic; 30 | clr_i: in std_logic; 31 | rst_i: in std_logic; 32 | 33 | we_i: in std_logic; 34 | d_i: in std_logic_vector(DATA_WIDTH-1 downto 0); 35 | re_i: in std_logic; 36 | d_o: out std_logic_vector(DATA_WIDTH-1 downto 0); 37 | 38 | empty_o: out std_logic; 39 | full_o: out std_logic; 40 | count_o: out std_logic_vector(ADDR_WIDTH downto 0) 41 | ); 42 | end entity; 43 | 44 | architecture rtl of sync_fifo is 45 | 46 | signal raddr: unsigned(ADDR_WIDTH-1 downto 0):=(others=>'0'); 47 | signal count: unsigned(ADDR_WIDTH downto 0):=(others=>'0'); 48 | signal waddr: unsigned(ADDR_WIDTH-1 downto 0); 49 | signal full: std_logic:='0'; 50 | signal empty: std_logic:='1'; 51 | 52 | signal we: std_logic; 53 | signal re: std_logic; 54 | 55 | signal raddr_next: unsigned(raddr'range); 56 | signal count_next: unsigned(count'range); 57 | 58 | signal ram_waddr: std_logic_vector(ADDR_WIDTH-1 downto 0); 59 | signal ram_raddr: std_logic_vector(ADDR_WIDTH-1 downto 0); 60 | signal ram_rdata: std_logic_vector(d_o'range); 61 | 62 | begin 63 | 64 | we<=we_i and not full; 65 | re<=re_i and not empty; 66 | 67 | raddr_next<=raddr+1 when re='1' else raddr; 68 | count_next<=count+1 when (we='1' and re='0') else count-1 when (we='0' and re='1') else count; 69 | 70 | process (clk_i,clr_i) is 71 | begin 72 | if clr_i='1' then 73 | raddr<=(others=>'0'); 74 | count<=(others=>'0'); 75 | full<='0'; 76 | empty<='1'; 77 | elsif rising_edge(clk_i) then 78 | if rst_i='1' then 79 | raddr<=(others=>'0'); 80 | count<=(others=>'0'); 81 | count_o<=(others=>'0'); 82 | full<='0'; 83 | empty<='1'; 84 | else 85 | raddr<=raddr_next; 86 | count<=count_next; 87 | -- To improve performance, in certain cases "empty_o" is asserted 88 | -- even though data are actually in RAM. "count_o" must also reflect that. 89 | count_o<=std_logic_vector(count_next); 90 | if count=to_unsigned(0,count'length) or (count=to_unsigned(1,count'length) and re='1') then 91 | empty<='1'; 92 | count_o<=(others=>'0'); 93 | else 94 | empty<='0'; 95 | end if; 96 | if SIZE=2**ADDR_WIDTH then 97 | full<=count_next(count_next'high); 98 | else 99 | if count_next=to_unsigned(SIZE,count_next'length) then 100 | full<='1'; 101 | else 102 | full<='0'; 103 | end if; 104 | end if; 105 | end if; 106 | end if; 107 | end process; 108 | 109 | waddr<=raddr+count(waddr'range); 110 | 111 | ram_waddr<=std_logic_vector(waddr); 112 | ram_raddr<=std_logic_vector(raddr_next); 113 | 114 | ram_inst: entity work.generic_dpram(rtl) 115 | generic map( 116 | DATA_WIDTH=>DATA_WIDTH, 117 | ADDR_WIDTH=>ADDR_WIDTH, 118 | SIZE=>2**ADDR_WIDTH, 119 | MODE=>"DONTCARE" 120 | ) 121 | port map( 122 | clka_i=>clk_i, 123 | cea_i=>'1', 124 | wea_i=>we, 125 | addra_i=>ram_waddr, 126 | da_i=>d_i, 127 | da_o=>open, 128 | 129 | clkb_i=>clk_i, 130 | ceb_i=>'1', 131 | addrb_i=>ram_raddr, 132 | db_o=>ram_rdata 133 | ); 134 | 135 | fwft_gen: if FWFT generate 136 | d_o<=ram_rdata; 137 | end generate; 138 | 139 | not_fwft_gen: if not FWFT generate 140 | process (clk_i) is 141 | begin 142 | if rising_edge(clk_i) then 143 | if re='1' then 144 | d_o<=ram_rdata; 145 | end if; 146 | end if; 147 | end process; 148 | end generate; 149 | 150 | full_o<=full; 151 | empty_o<=empty; 152 | 153 | end architecture; 154 | -------------------------------------------------------------------------------- /doc/src/logo/lxp32-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 47 | 55 | 56 | 58 | 59 | 61 | image/svg+xml 62 | 64 | 65 | 66 | 67 | 68 | 73 | 79 | 85 | 91 | 97 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /rtl/lxp32_interrupt_mux.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Interrupt multiplexer 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Manages LXP32 interrupts. Interrupts with lower numbers have 9 | -- higher priority. 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; 15 | 16 | entity lxp32_interrupt_mux is 17 | port( 18 | clk_i: in std_logic; 19 | rst_i: in std_logic; 20 | 21 | irq_i: in std_logic_vector(7 downto 0); 22 | 23 | interrupt_valid_o: out std_logic; 24 | interrupt_vector_o: out std_logic_vector(2 downto 0); 25 | interrupt_ready_i: in std_logic; 26 | interrupt_return_i: in std_logic; 27 | 28 | wakeup_o: out std_logic; 29 | 30 | sp_waddr_i: in std_logic_vector(7 downto 0); 31 | sp_we_i: in std_logic; 32 | sp_wdata_i: in std_logic_vector(31 downto 0) 33 | ); 34 | end entity; 35 | 36 | architecture rtl of lxp32_interrupt_mux is 37 | 38 | signal irq: std_logic_vector(irq_i'range); 39 | signal irq_reg: std_logic_vector(irq_i'range):=(others=>'0'); 40 | 41 | type state_type is (Ready,Requested,WaitForExit); 42 | signal state: state_type:=Ready; 43 | 44 | signal pending_interrupts: std_logic_vector(irq_i'range):=(others=>'0'); 45 | 46 | signal interrupt_valid: std_logic:='0'; 47 | 48 | signal interrupts_enabled: std_logic_vector(7 downto 0):=(others=>'0'); 49 | signal interrupts_wakeup: std_logic_vector(7 downto 0):=(others=>'0'); 50 | signal interrupts_level: std_logic_vector(7 downto 0):=(others=>'0'); 51 | signal interrupts_invert: std_logic_vector(7 downto 0):=(others=>'0'); 52 | 53 | begin 54 | 55 | irq<=irq_i xor interrupts_invert; 56 | 57 | -- Note: "disabled" interrupts (i.e. for which interrupts_enabled_i(i)='0') 58 | -- are ignored completely, meaning that the interrupt handler won't be 59 | -- called even if the interrupt is enabled later. 60 | 61 | process (clk_i) is 62 | begin 63 | if rising_edge(clk_i) then 64 | if rst_i='1' then 65 | irq_reg<=(others=>'0'); 66 | pending_interrupts<=(others=>'0'); 67 | state<=Ready; 68 | interrupt_valid<='0'; 69 | interrupt_vector_o<=(others=>'-'); 70 | wakeup_o<='0'; 71 | else 72 | irq_reg<=irq; 73 | 74 | pending_interrupts<=(pending_interrupts or 75 | (irq and not irq_reg)) and not interrupts_level and 76 | interrupts_enabled and not interrupts_wakeup; 77 | 78 | case state is 79 | when Ready => 80 | for i in irq'reverse_range loop -- lower interrupts have priority 81 | if (interrupts_level(i)='0' and pending_interrupts(i)='1') or 82 | (interrupts_level(i)='1' and irq(i)='1' and 83 | interrupts_enabled(i)='1' and interrupts_wakeup(i)='0') then 84 | pending_interrupts(i)<='0'; 85 | interrupt_valid<='1'; 86 | interrupt_vector_o<=std_logic_vector(to_unsigned(i,3)); 87 | state<=Requested; 88 | exit; 89 | end if; 90 | end loop; 91 | when Requested => 92 | if interrupt_ready_i='1' then 93 | interrupt_valid<='0'; 94 | state<=WaitForExit; 95 | end if; 96 | when WaitForExit => 97 | if interrupt_return_i='1' then 98 | state<=Ready; 99 | end if; 100 | end case; 101 | 102 | wakeup_o<='0'; 103 | for i in irq'range loop 104 | if interrupts_enabled(i)='1' and interrupts_wakeup(i)='1' then 105 | if interrupts_level(i)='0' then -- edge triggered 106 | if irq(i)='1' and irq_reg(i)='0' then 107 | wakeup_o<='1'; 108 | end if; 109 | else -- level triggered 110 | if irq(i)='1' then 111 | wakeup_o<='1'; 112 | end if; 113 | end if; 114 | end if; 115 | end loop; 116 | end if; 117 | end if; 118 | end process; 119 | 120 | interrupt_valid_o<=interrupt_valid; 121 | 122 | process (clk_i) is 123 | begin 124 | if rising_edge(clk_i) then 125 | if rst_i='1' then 126 | interrupts_enabled<=(others=>'0'); 127 | interrupts_wakeup<=(others=>'0'); 128 | interrupts_level<=(others=>'0'); 129 | interrupts_invert<=(others=>'0'); 130 | elsif sp_we_i='1' and sp_waddr_i=X"FC" then 131 | interrupts_enabled<=sp_wdata_i(7 downto 0); 132 | interrupts_wakeup<=sp_wdata_i(15 downto 8); 133 | interrupts_level<=sp_wdata_i(23 downto 16); 134 | interrupts_invert<=sp_wdata_i(31 downto 24); 135 | end if; 136 | end if; 137 | end process; 138 | 139 | end architecture; 140 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/generic_dpram.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Generic FPGA memory block 3 | -- 4 | -- Copyright (c) 2015 by Alex I. Kuznetsov 5 | -- 6 | -- Portable description of a dual-port memory block with one write 7 | -- port. 8 | -- 9 | -- Parameters: 10 | -- * DATA_WIDTH: data port width 11 | -- * ADDR_WIDTH: address port width 12 | -- * SIZE: memory size 13 | -- * MODE: read/write synchronization mode for port A 14 | -- DONTCARE: choose the most efficient design 15 | -- WR_FIRST: feed written value to the output 16 | -- RD_FIRST: read old value 17 | -- NOCHANGE: don't change output during write 18 | --------------------------------------------------------------------- 19 | 20 | library ieee; 21 | use ieee.std_logic_1164.all; 22 | use ieee.numeric_std.all; 23 | 24 | entity generic_dpram is 25 | generic( 26 | DATA_WIDTH: integer; 27 | ADDR_WIDTH: integer; 28 | SIZE: integer; 29 | MODE: string:="DONTCARE" 30 | ); 31 | port( 32 | clka_i: in std_logic; 33 | cea_i: in std_logic; 34 | wea_i: in std_logic; 35 | addra_i: in std_logic_vector(ADDR_WIDTH-1 downto 0); 36 | da_i: in std_logic_vector(DATA_WIDTH-1 downto 0); 37 | da_o: out std_logic_vector(DATA_WIDTH-1 downto 0); 38 | 39 | clkb_i: in std_logic; 40 | ceb_i: in std_logic; 41 | addrb_i: in std_logic_vector(ADDR_WIDTH-1 downto 0); 42 | db_o: out std_logic_vector(DATA_WIDTH-1 downto 0) 43 | ); 44 | end entity; 45 | 46 | architecture rtl of generic_dpram is 47 | 48 | type ram_type is array(SIZE-1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0); 49 | signal ram: ram_type; 50 | 51 | attribute syn_ramstyle: string; 52 | attribute syn_ramstyle of ram: signal is "no_rw_check"; 53 | attribute ram_style: string; -- for Xilinx 54 | attribute ram_style of ram: signal is "block"; 55 | 56 | begin 57 | 58 | -- Ensure that generics have valid values 59 | 60 | assert SIZE<=2**ADDR_WIDTH 61 | report "SIZE must be less or equal than 2^ADDR_WIDTH" 62 | severity failure; 63 | 64 | assert MODE="DONTCARE" or MODE="WR_FIRST" or MODE="RD_FIRST" or MODE="NOCHANGE" 65 | report "Unrecognized MODE value (DONTCARE, WR_FIRST, RD_FIRST or NOCHANGE expected)" 66 | severity failure; 67 | 68 | -- Port A (read/write) 69 | 70 | port_a_dont_care_gen: if MODE="DONTCARE" generate 71 | process (clka_i) is 72 | begin 73 | if rising_edge(clka_i) then 74 | if cea_i='1' then 75 | if wea_i='1' then 76 | ram(to_integer(unsigned(addra_i)))<=da_i; 77 | da_o<=(others=>'-'); 78 | else 79 | if is_x(addra_i) then 80 | da_o<=(others=>'X'); 81 | else 82 | da_o<=ram(to_integer(unsigned(addra_i))); 83 | end if; 84 | end if; 85 | end if; 86 | end if; 87 | end process; 88 | end generate; 89 | 90 | port_a_write_first_gen: if MODE="WR_FIRST" generate 91 | process (clka_i) is 92 | begin 93 | if rising_edge(clka_i) then 94 | if cea_i='1' then 95 | if wea_i='1' then 96 | ram(to_integer(unsigned(addra_i)))<=da_i; 97 | da_o<=da_i; 98 | else 99 | if is_x(addra_i) then 100 | da_o<=(others=>'X'); 101 | else 102 | da_o<=ram(to_integer(unsigned(addra_i))); 103 | end if; 104 | end if; 105 | end if; 106 | end if; 107 | end process; 108 | end generate; 109 | 110 | port_a_read_first_gen: if MODE="RD_FIRST" generate 111 | process (clka_i) is 112 | begin 113 | if rising_edge(clka_i) then 114 | if cea_i='1' then 115 | if wea_i='1' then 116 | ram(to_integer(unsigned(addra_i)))<=da_i; 117 | end if; 118 | if is_x(addra_i) then 119 | da_o<=(others=>'X'); 120 | else 121 | da_o<=ram(to_integer(unsigned(addra_i))); 122 | end if; 123 | end if; 124 | end if; 125 | end process; 126 | end generate; 127 | 128 | port_a_no_change_gen: if MODE="NOCHANGE" generate 129 | process (clka_i) is 130 | begin 131 | if rising_edge(clka_i) then 132 | if cea_i='1' then 133 | if wea_i='1' then 134 | ram(to_integer(unsigned(addra_i)))<=da_i; 135 | else 136 | if is_x(addra_i) then 137 | da_o<=(others=>'X'); 138 | else 139 | da_o<=ram(to_integer(unsigned(addra_i))); 140 | end if; 141 | end if; 142 | end if; 143 | end if; 144 | end process; 145 | end generate; 146 | 147 | -- Port B (read only) 148 | 149 | process (clkb_i) is 150 | begin 151 | if rising_edge(clkb_i) then 152 | if ceb_i='1' then 153 | if is_x(addrb_i) then 154 | db_o<=(others=>'X'); 155 | else 156 | db_o<=ram(to_integer(unsigned(addrb_i))); 157 | end if; 158 | end if; 159 | end if; 160 | end process; 161 | 162 | end architecture; 163 | -------------------------------------------------------------------------------- /rtl/lxp32_dbus.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- DBUS master 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Manages data bus (DBUS) access. 9 | --------------------------------------------------------------------- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; 14 | 15 | entity lxp32_dbus is 16 | generic( 17 | RMW: boolean 18 | ); 19 | port( 20 | clk_i: in std_logic; 21 | rst_i: in std_logic; 22 | 23 | valid_i: in std_logic; 24 | 25 | cmd_dbus_i: in std_logic; 26 | cmd_dbus_store_i: in std_logic; 27 | cmd_dbus_byte_i: in std_logic; 28 | cmd_signed_i: in std_logic; 29 | addr_i: in std_logic_vector(31 downto 0); 30 | wdata_i: in std_logic_vector(31 downto 0); 31 | 32 | rdata_o: out std_logic_vector(31 downto 0); 33 | we_o: out std_logic; 34 | busy_o: out std_logic; 35 | 36 | dbus_cyc_o: out std_logic; 37 | dbus_stb_o: out std_logic; 38 | dbus_we_o: out std_logic; 39 | dbus_sel_o: out std_logic_vector(3 downto 0); 40 | dbus_ack_i: in std_logic; 41 | dbus_adr_o: out std_logic_vector(31 downto 2); 42 | dbus_dat_o: out std_logic_vector(31 downto 0); 43 | dbus_dat_i: in std_logic_vector(31 downto 0) 44 | ); 45 | end entity; 46 | 47 | architecture rtl of lxp32_dbus is 48 | 49 | signal strobe: std_logic:='0'; 50 | signal we_out: std_logic:='0'; 51 | signal we: std_logic; 52 | signal byte_mode: std_logic; 53 | signal sel: std_logic_vector(3 downto 0); 54 | signal sig: std_logic; 55 | signal rmw_mode: std_logic; 56 | 57 | signal dbus_rdata: std_logic_vector(31 downto 0); 58 | signal selected_byte: std_logic_vector(7 downto 0); 59 | 60 | begin 61 | 62 | process (clk_i) is 63 | begin 64 | if rising_edge(clk_i) then 65 | if rst_i='1' then 66 | we_out<='0'; 67 | strobe<='0'; 68 | sig<='-'; 69 | byte_mode<='-'; 70 | sel<=(others=>'-'); 71 | we<='-'; 72 | rmw_mode<='-'; 73 | dbus_adr_o<=(others=>'-'); 74 | dbus_dat_o<=(others=>'-'); 75 | else 76 | we_out<='0'; 77 | if strobe='0' then 78 | if valid_i='1' and cmd_dbus_i='1' then 79 | strobe<='1'; 80 | sig<=cmd_signed_i; 81 | 82 | dbus_adr_o<=addr_i(31 downto 2); 83 | 84 | if cmd_dbus_byte_i='0' then 85 | byte_mode<='0'; 86 | dbus_dat_o<=wdata_i; 87 | sel<="1111"; 88 | 89 | -- synthesis translate_off 90 | assert addr_i(1 downto 0)="00" 91 | report "Misaligned word-granular access on data bus" 92 | severity warning; 93 | -- synthesis translate_on 94 | else 95 | byte_mode<='1'; 96 | dbus_dat_o<=wdata_i(7 downto 0)&wdata_i(7 downto 0)& 97 | wdata_i(7 downto 0)&wdata_i(7 downto 0); 98 | 99 | case addr_i(1 downto 0) is 100 | when "00" => sel<="0001"; 101 | when "01" => sel<="0010"; 102 | when "10" => sel<="0100"; 103 | when "11" => sel<="1000"; 104 | when others => 105 | end case; 106 | end if; 107 | 108 | if not RMW then 109 | we<=cmd_dbus_store_i; 110 | rmw_mode<='0'; 111 | else 112 | we<=cmd_dbus_store_i and not cmd_dbus_byte_i; 113 | rmw_mode<=cmd_dbus_store_i and cmd_dbus_byte_i; 114 | end if; 115 | end if; 116 | else 117 | if dbus_ack_i='1' then 118 | if rmw_mode='1' and we='0' and RMW then 119 | we<='1'; 120 | for i in sel'range loop 121 | if sel(i)='0' then 122 | dbus_dat_o(i*8+7 downto i*8)<= 123 | dbus_dat_i(i*8+7 downto i*8); 124 | end if; 125 | end loop; 126 | else 127 | strobe<='0'; 128 | if we='0' then 129 | we_out<='1'; 130 | end if; 131 | end if; 132 | end if; 133 | end if; 134 | end if; 135 | end if; 136 | end process; 137 | 138 | dbus_cyc_o<=strobe; 139 | dbus_stb_o<=strobe; 140 | dbus_we_o<=we; 141 | 142 | sel_no_rmw_gen: if not RMW generate 143 | dbus_sel_o<=sel; 144 | end generate; 145 | 146 | sel_rmw_gen: if RMW generate 147 | dbus_sel_o<=(others=>'1'); 148 | end generate; 149 | 150 | process (clk_i) is 151 | begin 152 | if rising_edge(clk_i) then 153 | dbus_rdata<=dbus_dat_i; 154 | end if; 155 | end process; 156 | 157 | selected_byte_gen: for i in selected_byte'range generate 158 | selected_byte(i)<=(dbus_rdata(i) and sel(0)) or 159 | (dbus_rdata(i+8) and sel(1)) or 160 | (dbus_rdata(i+16) and sel(2)) or 161 | (dbus_rdata(i+24) and sel(3)); 162 | end generate; 163 | 164 | rdata_o<=dbus_rdata when byte_mode='0' else 165 | X"000000"&selected_byte when selected_byte(selected_byte'high)='0' or sig='0' else 166 | X"FFFFFF"&selected_byte; 167 | 168 | we_o<=we_out; 169 | busy_o<=strobe or we_out; 170 | 171 | end architecture; 172 | -------------------------------------------------------------------------------- /rtl/lxp32_divider.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Divider 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Based on the NRD (Non Restoring Division) algorithm. Takes 9 | -- 36 cycles to calculate quotient (37 for remainder). 10 | --------------------------------------------------------------------- 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; 15 | 16 | entity lxp32_divider is 17 | port( 18 | clk_i: in std_logic; 19 | rst_i: in std_logic; 20 | ce_i: in std_logic; 21 | op1_i: in std_logic_vector(31 downto 0); 22 | op2_i: in std_logic_vector(31 downto 0); 23 | signed_i: in std_logic; 24 | rem_i: in std_logic; 25 | ce_o: out std_logic; 26 | result_o: out std_logic_vector(31 downto 0) 27 | ); 28 | end entity; 29 | 30 | architecture rtl of lxp32_divider is 31 | 32 | -- Complementor signals 33 | 34 | signal compl_inv: std_logic; 35 | signal compl_mux: std_logic_vector(31 downto 0); 36 | signal compl_out: std_logic_vector(31 downto 0); 37 | 38 | signal inv_res: std_logic; 39 | 40 | -- Divider FSM signals 41 | 42 | signal fsm_ce: std_logic:='0'; 43 | 44 | signal dividend: unsigned(31 downto 0); 45 | signal divisor: unsigned(32 downto 0); 46 | signal want_remainder: std_logic; 47 | 48 | signal partial_remainder: unsigned(32 downto 0); 49 | signal addend: unsigned(32 downto 0); 50 | signal sum: unsigned(32 downto 0); 51 | signal sum_positive: std_logic; 52 | signal sum_subtract: std_logic; 53 | 54 | signal cnt: integer range 0 to 34:=0; 55 | 56 | signal ceo: std_logic:='0'; 57 | 58 | -- Output restoration signals 59 | 60 | signal remainder_corrector: unsigned(31 downto 0); 61 | signal remainder_corrector_1: std_logic; 62 | signal remainder_pos: unsigned(31 downto 0); 63 | signal result_pos: unsigned(31 downto 0); 64 | 65 | begin 66 | 67 | compl_inv<=op1_i(31) and signed_i when ce_i='1' else inv_res; 68 | compl_mux<=op1_i when ce_i='1' else std_logic_vector(result_pos); 69 | 70 | compl_op1_inst: entity work.lxp32_compl(rtl) 71 | port map( 72 | clk_i=>clk_i, 73 | compl_i=>compl_inv, 74 | d_i=>compl_mux, 75 | d_o=>compl_out 76 | ); 77 | 78 | process (clk_i) is 79 | begin 80 | if rising_edge(clk_i) then 81 | if rst_i='1' then 82 | fsm_ce<='0'; 83 | want_remainder<='-'; 84 | inv_res<='-'; 85 | else 86 | fsm_ce<=ce_i; 87 | if ce_i='1' then 88 | want_remainder<=rem_i; 89 | if rem_i='1' then 90 | inv_res<=op1_i(31) and signed_i; 91 | else 92 | inv_res<=(op1_i(31) xor op2_i(31)) and signed_i; 93 | end if; 94 | end if; 95 | end if; 96 | end if; 97 | end process; 98 | 99 | -- Main adder/subtractor 100 | 101 | addend_gen: for i in addend'range generate 102 | addend(i)<=divisor(i) xor sum_subtract; 103 | end generate; 104 | 105 | sum<=partial_remainder+addend+(to_unsigned(0,32)&sum_subtract); 106 | sum_positive<=not sum(32); 107 | 108 | -- Divider state machine 109 | 110 | process (clk_i) is 111 | begin 112 | if rising_edge(clk_i) then 113 | if rst_i='1' then 114 | cnt<=0; 115 | ceo<='0'; 116 | divisor<=(others=>'-'); 117 | dividend<=(others=>'-'); 118 | partial_remainder<=(others=>'-'); 119 | sum_subtract<='-'; 120 | else 121 | if cnt=1 then 122 | ceo<='1'; 123 | else 124 | ceo<='0'; 125 | end if; 126 | 127 | if ce_i='1' then 128 | divisor(31 downto 0)<=unsigned(op2_i); 129 | divisor(32)<=op2_i(31) and signed_i; 130 | end if; 131 | 132 | if fsm_ce='1' then 133 | dividend<=unsigned(compl_out(30 downto 0)&"0"); 134 | partial_remainder<=to_unsigned(0,32)&compl_out(31); 135 | sum_subtract<=not divisor(32); 136 | if want_remainder='1' then 137 | cnt<=34; 138 | else 139 | cnt<=33; 140 | end if; 141 | else 142 | partial_remainder<=sum(31 downto 0)÷nd(31); 143 | sum_subtract<=sum_positive xor divisor(32); 144 | dividend<=dividend(30 downto 0)&sum_positive; 145 | if cnt>0 then 146 | cnt<=cnt-1; 147 | end if; 148 | end if; 149 | end if; 150 | end if; 151 | end process; 152 | 153 | -- Output restoration circuit 154 | 155 | process (clk_i) is 156 | begin 157 | if rising_edge(clk_i) then 158 | for i in remainder_corrector'range loop 159 | remainder_corrector(i)<=(divisor(i) xor divisor(32)) and not sum_positive; 160 | end loop; 161 | remainder_corrector_1<=divisor(32) and not sum_positive; 162 | remainder_pos<=partial_remainder(32 downto 1)+remainder_corrector+ 163 | (to_unsigned(0,31)&remainder_corrector_1); 164 | end if; 165 | end process; 166 | 167 | result_pos<=remainder_pos when want_remainder='1' else dividend; 168 | 169 | result_o<=compl_out; 170 | ce_o<=ceo; 171 | 172 | end architecture; 173 | -------------------------------------------------------------------------------- /verify/icache/src/tb/cpu_model.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- CPU model 3 | -- 4 | -- Part of the LXP32 instruction cache testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Requests data from cache 9 | --------------------------------------------------------------------- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; 14 | use ieee.math_real.all; 15 | 16 | use work.common_pkg.all; 17 | use work.tb_pkg.all; 18 | 19 | entity cpu_model is 20 | generic( 21 | BLOCKS: integer; 22 | VERBOSE: boolean 23 | ); 24 | port( 25 | clk_i: in std_logic; 26 | 27 | lli_re_o: out std_logic; 28 | lli_adr_o: out std_logic_vector(29 downto 0); 29 | lli_dat_i: in std_logic_vector(31 downto 0); 30 | lli_busy_i: in std_logic; 31 | 32 | finish_o: out std_logic 33 | ); 34 | end entity; 35 | 36 | architecture sim of cpu_model is 37 | 38 | constant bursts: integer:=10000; 39 | 40 | signal re: std_logic:='0'; 41 | signal lli_adr: std_logic_vector(29 downto 0); 42 | 43 | signal request: std_logic:='0'; 44 | signal request_addr: std_logic_vector(29 downto 0); 45 | 46 | signal finish: std_logic:='0'; 47 | 48 | signal current_latency: integer:=1; 49 | signal max_latency: integer:=-1; 50 | signal total_latency: integer:=0; 51 | signal spurious_misses: integer:=0; 52 | 53 | begin 54 | 55 | process is 56 | variable b: integer:=1; 57 | variable start: integer; 58 | variable size: integer; 59 | variable addr: integer:=0; 60 | variable delay: integer; 61 | variable rng_state: rng_state_type; 62 | variable r: integer; 63 | variable total_requests: integer:=0; 64 | begin 65 | while b<=BLOCKS loop 66 | rand(rng_state,1,10,r); 67 | if r=1 then -- insert large block occasionally 68 | rand(rng_state,1,400,size); 69 | else -- small block 70 | rand(rng_state,1,32,size); 71 | end if; 72 | 73 | rand(rng_state,0,1,r); 74 | if r=0 then -- long jump 75 | rand(rng_state,0,1024,start); 76 | addr:=start; 77 | if VERBOSE then 78 | report "Fetching block #"&integer'image(b)&" at address "&integer'image(addr)& 79 | " of size "&integer'image(size); 80 | end if; 81 | else -- short jump 82 | rand(rng_state,-10,10,r); 83 | start:=addr+r; 84 | if start<0 then 85 | start:=0; 86 | end if; 87 | addr:=start; 88 | if VERBOSE then 89 | report "Fetching block #"&integer'image(b)&" at address "&integer'image(addr)& 90 | " of size "&integer'image(size)&" (short jump)"; 91 | end if; 92 | end if; 93 | 94 | while addr0 then 103 | for i in 1 to delay loop 104 | wait until rising_edge(clk_i); 105 | end loop; 106 | end if; 107 | end loop; 108 | 109 | if (b mod 10000)=0 then 110 | report integer'image(b)&" BLOCKS PROCESSED"; 111 | end if; 112 | 113 | b:=b+1; 114 | end loop; 115 | 116 | report "Number of requests: "&integer'image(total_requests); 117 | report "Maximum latency: "&integer'image(max_latency); 118 | report "Average latency: "&real'image(real(total_latency)/real(total_requests)); 119 | report "Number of spurious misses: "&integer'image(spurious_misses); 120 | 121 | finish<='1'; 122 | wait; 123 | end process; 124 | 125 | lli_re_o<=re; 126 | lli_adr_o<=lli_adr; 127 | 128 | process (clk_i) is 129 | begin 130 | if rising_edge(clk_i) then 131 | if lli_busy_i='0' then 132 | if request='1' then 133 | assert lli_dat_i=(("00"&request_addr) xor xor_constant) 134 | report "Data mismatch: expected 0x"& 135 | hex_string(("00"&request_addr) xor xor_constant)& 136 | ", got 0x"&hex_string(lli_dat_i) 137 | severity failure; 138 | end if; 139 | 140 | request<=re; 141 | request_addr<=lli_adr; 142 | end if; 143 | end if; 144 | end process; 145 | 146 | finish_o<=finish; 147 | 148 | -- Measure latency 149 | 150 | process (clk_i) is 151 | begin 152 | if rising_edge(clk_i) then 153 | if lli_busy_i='0' then 154 | if request='1' then 155 | total_latency<=total_latency+current_latency; 156 | if current_latency>max_latency then 157 | max_latency<=current_latency; 158 | end if; 159 | end if; 160 | current_latency<=1; 161 | else 162 | if lli_dat_i=(("00"&request_addr) xor xor_constant) and current_latency=1 then 163 | spurious_misses<=spurious_misses+1; 164 | end if; 165 | current_latency<=current_latency+1; 166 | end if; 167 | end if; 168 | end process; 169 | 170 | process (clk_i) is 171 | begin 172 | if rising_edge(clk_i) then 173 | assert lli_busy_i='0' or request='1' 174 | report "LLI busy signal asserted without a request" 175 | severity failure; 176 | end if; 177 | end process; 178 | 179 | end architecture; 180 | -------------------------------------------------------------------------------- /rtl/lxp32_mul_opt.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Optimized multiplier 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- This multiplier is designed for technologies that don't provide 9 | -- fast 16x16 multipliers. One multiplication takes 6 cycles. 10 | -- 11 | -- The multiplication algorithm is based on carry-save accumulation 12 | -- of partial products. 13 | --------------------------------------------------------------------- 14 | 15 | library ieee; 16 | use ieee.std_logic_1164.all; 17 | use ieee.numeric_std.all; 18 | 19 | entity lxp32_mul_opt is 20 | port( 21 | clk_i: in std_logic; 22 | rst_i: in std_logic; 23 | ce_i: in std_logic; 24 | op1_i: in std_logic_vector(31 downto 0); 25 | op2_i: in std_logic_vector(31 downto 0); 26 | ce_o: out std_logic; 27 | result_o: out std_logic_vector(31 downto 0) 28 | ); 29 | end entity; 30 | 31 | architecture rtl of lxp32_mul_opt is 32 | 33 | function csa_sum(a: unsigned; b: unsigned; c: unsigned; n: integer) return unsigned is 34 | variable r: unsigned(n-1 downto 0); 35 | begin 36 | for i in r'range loop 37 | r(i):=a(i) xor b(i) xor c(i); 38 | end loop; 39 | return r; 40 | end function; 41 | 42 | function csa_carry(a: unsigned; b: unsigned; c: unsigned; n: integer) return unsigned is 43 | variable r: unsigned(n-1 downto 0); 44 | begin 45 | for i in r'range loop 46 | r(i):=(a(i) and b(i)) or (a(i) and c(i)) or (b(i) and c(i)); 47 | end loop; 48 | return r&"0"; 49 | end function; 50 | 51 | signal reg1: unsigned(op1_i'range); 52 | signal reg2: unsigned(op2_i'range); 53 | 54 | type pp_type is array (7 downto 0) of unsigned(31 downto 0); 55 | signal pp: pp_type; 56 | 57 | type pp_sum_type is array (7 downto 0) of unsigned(31 downto 0); 58 | signal pp_sum: pp_sum_type; 59 | 60 | type pp_carry_type is array (7 downto 0) of unsigned(32 downto 0); 61 | signal pp_carry: pp_carry_type; 62 | 63 | signal acc_sum: unsigned(31 downto 0); 64 | signal acc_carry: unsigned(31 downto 0); 65 | 66 | signal cnt: integer range 0 to 4:=0; 67 | 68 | signal result: std_logic_vector(result_o'range); 69 | signal ceo: std_logic:='0'; 70 | 71 | begin 72 | 73 | -- Calculate 8 partial products in parallel 74 | 75 | pp_gen: for i in pp'range generate 76 | pp(i)<=shift_left(reg1,i) when reg2(i)='1' else (others=>'0'); 77 | end generate; 78 | 79 | -- Add partial products to the accumulator using carry-save adder tree 80 | 81 | pp_sum(0)<=csa_sum(pp(0),pp(1),pp(2),32); 82 | pp_carry(0)<=csa_carry(pp(0),pp(1),pp(2),32); 83 | 84 | pp_sum(1)<=csa_sum(pp(3),pp(4),pp(5),32); 85 | pp_carry(1)<=csa_carry(pp(3),pp(4),pp(5),32); 86 | 87 | pp_sum(2)<=csa_sum(pp(6),pp(7),acc_sum,32); 88 | pp_carry(2)<=csa_carry(pp(6),pp(7),acc_sum,32); 89 | 90 | pp_sum(3)<=csa_sum(pp_sum(0),pp_carry(0),pp_sum(1),32); 91 | pp_carry(3)<=csa_carry(pp_sum(0),pp_carry(0),pp_sum(1),32); 92 | 93 | pp_sum(4)<=csa_sum(pp_carry(1),pp_sum(2),pp_carry(2),32); 94 | pp_carry(4)<=csa_carry(pp_carry(1),pp_sum(2),pp_carry(2),32); 95 | 96 | pp_sum(5)<=csa_sum(pp_sum(3),pp_carry(3),pp_sum(4),32); 97 | pp_carry(5)<=csa_carry(pp_sum(3),pp_carry(3),pp_sum(4),32); 98 | 99 | pp_sum(6)<=csa_sum(pp_sum(5),pp_carry(5),pp_carry(4),32); 100 | pp_carry(6)<=csa_carry(pp_sum(5),pp_carry(5),pp_carry(4),32); 101 | 102 | pp_sum(7)<=csa_sum(pp_sum(6),pp_carry(6),acc_carry,32); 103 | pp_carry(7)<=csa_carry(pp_sum(6),pp_carry(6),acc_carry,32); 104 | 105 | -- Multiplier state machine 106 | 107 | process (clk_i) is 108 | begin 109 | if rising_edge(clk_i) then 110 | if rst_i='1' then 111 | ceo<='0'; 112 | cnt<=0; 113 | reg1<=(others=>'-'); 114 | reg2<=(others=>'-'); 115 | acc_sum<=(others=>'-'); 116 | acc_carry<=(others=>'-'); 117 | else 118 | if cnt=1 then 119 | ceo<='1'; 120 | else 121 | ceo<='0'; 122 | end if; 123 | 124 | if ce_i='1' then 125 | cnt<=4; 126 | reg1<=unsigned(op1_i); 127 | reg2<=unsigned(op2_i); 128 | acc_sum<=(others=>'0'); 129 | acc_carry<=(others=>'0'); 130 | else 131 | acc_sum<=pp_sum(7); 132 | acc_carry<=pp_carry(7)(acc_carry'range); 133 | reg1<=reg1(reg1'high-8 downto 0)&X"00"; 134 | reg2<=X"00"®2(reg2'high downto 8); 135 | if cnt>0 then 136 | cnt<=cnt-1; 137 | end if; 138 | end if; 139 | end if; 140 | end if; 141 | end process; 142 | 143 | result<=std_logic_vector(acc_sum+acc_carry); 144 | 145 | result_o<=result; 146 | ce_o<=ceo; 147 | 148 | -- A simulation-time multiplication check 149 | 150 | -- synthesis translate_off 151 | 152 | process (clk_i) is 153 | variable p: unsigned(op1_i'length+op2_i'length-1 downto 0); 154 | begin 155 | if rising_edge(clk_i) then 156 | if ce_i='1' then 157 | p:=unsigned(op1_i)*unsigned(op2_i); 158 | elsif ceo='1' then 159 | assert result=std_logic_vector(p(result'range)) 160 | report "Incorrect multiplication result" 161 | severity failure; 162 | end if; 163 | end if; 164 | end process; 165 | 166 | -- synthesis translate_on 167 | 168 | end architecture; 169 | -------------------------------------------------------------------------------- /verify/lxp32/src/tb/tb.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32 verification environment (self-checking testbench) 3 | -- 4 | -- Part of the LXP32 testbench 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- Simulates LXP32 test platform, verifies results. 9 | -- 10 | -- Parameters: 11 | -- CPU_DBUS_RMW: DBUS_RMW CPU generic 12 | -- CPU_MUL_ARCH: MUL_ARCH CPU generic 13 | -- MODEL_LXP32C: when true, simulates LXP32C variant (with 14 | -- instruction cache), otherwise LXP32U 15 | -- TEST_CASE: If non-empty, selects a test case to run. 16 | -- If empty, all tests are executed. 17 | -- THROTTLE_IBUS: perform pseudo-random instruction bus 18 | -- throttling 19 | -- THROTTLE_DBUS: perform pseudo-random data bus throttling 20 | -- VERBOSE: report everything that is written to the 21 | -- test monitor address space 22 | --------------------------------------------------------------------- 23 | 24 | use std.textio.all; 25 | 26 | library ieee; 27 | use ieee.std_logic_1164.all; 28 | use ieee.numeric_std.all; 29 | 30 | use work.tb_pkg.all; 31 | 32 | entity tb is 33 | generic( 34 | CPU_DBUS_RMW: boolean:=false; 35 | CPU_MUL_ARCH: string:="dsp"; 36 | MODEL_LXP32C: boolean:=true; 37 | TEST_CASE: string:=""; 38 | THROTTLE_DBUS: boolean:=true; 39 | THROTTLE_IBUS: boolean:=true; 40 | VERBOSE: boolean:=false 41 | ); 42 | end entity; 43 | 44 | architecture testbench of tb is 45 | 46 | signal clk: std_logic:='0'; 47 | 48 | signal globals: soc_globals_type:=(others=>'1'); 49 | signal soc_wbs_in: soc_wbs_in_type; 50 | signal soc_wbs_out: soc_wbs_out_type; 51 | signal soc_wbm_in: soc_wbm_in_type; 52 | signal soc_wbm_out: soc_wbm_out_type; 53 | 54 | signal monitor_out: monitor_out_type; 55 | 56 | signal finish: std_logic:='0'; 57 | 58 | begin 59 | 60 | dut: entity work.platform(rtl) 61 | generic map( 62 | CPU_DBUS_RMW=>CPU_DBUS_RMW, 63 | CPU_MUL_ARCH=>CPU_MUL_ARCH, 64 | MODEL_LXP32C=>MODEL_LXP32C, 65 | THROTTLE_DBUS=>THROTTLE_DBUS, 66 | THROTTLE_IBUS=>THROTTLE_IBUS 67 | ) 68 | port map( 69 | clk_i=>clk, 70 | rst_i=>globals.rst_i, 71 | cpu_rst_i=>globals.cpu_rst_i, 72 | 73 | wbm_cyc_o=>soc_wbm_out.cyc, 74 | wbm_stb_o=>soc_wbm_out.stb, 75 | wbm_we_o=>soc_wbm_out.we, 76 | wbm_sel_o=>soc_wbm_out.sel, 77 | wbm_ack_i=>soc_wbm_in.ack, 78 | wbm_adr_o=>soc_wbm_out.adr, 79 | wbm_dat_o=>soc_wbm_out.dat, 80 | wbm_dat_i=>soc_wbm_in.dat, 81 | 82 | wbs_cyc_i=>soc_wbs_in.cyc, 83 | wbs_stb_i=>soc_wbs_in.stb, 84 | wbs_we_i=>soc_wbs_in.we, 85 | wbs_sel_i=>soc_wbs_in.sel, 86 | wbs_ack_o=>soc_wbs_out.ack, 87 | wbs_adr_i=>soc_wbs_in.adr, 88 | wbs_dat_i=>soc_wbs_in.dat, 89 | wbs_dat_o=>soc_wbs_out.dat 90 | ); 91 | 92 | monitor_inst: entity work.monitor(sim) 93 | generic map( 94 | VERBOSE=>VERBOSE 95 | ) 96 | port map( 97 | clk_i=>clk, 98 | rst_i=>globals.rst_i, 99 | 100 | wbs_cyc_i=>soc_wbm_out.cyc, 101 | wbs_stb_i=>soc_wbm_out.stb, 102 | wbs_we_i=>soc_wbm_out.we, 103 | wbs_sel_i=>soc_wbm_out.sel, 104 | wbs_ack_o=>soc_wbm_in.ack, 105 | wbs_adr_i=>soc_wbm_out.adr, 106 | wbs_dat_i=>soc_wbm_out.dat, 107 | wbs_dat_o=>soc_wbm_in.dat, 108 | 109 | finished_o=>monitor_out.valid, 110 | result_o=>monitor_out.data 111 | ); 112 | 113 | clk<=not clk and not finish after 5 ns; 114 | 115 | process is 116 | begin 117 | if TEST_CASE'length=0 then 118 | run_test("test001.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 119 | run_test("test002.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 120 | run_test("test003.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 121 | run_test("test004.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 122 | run_test("test005.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 123 | run_test("test006.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 124 | run_test("test007.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 125 | run_test("test008.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 126 | run_test("test009.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 127 | run_test("test010.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 128 | run_test("test011.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 129 | run_test("test012.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 130 | run_test("test013.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 131 | run_test("test014.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 132 | run_test("test015.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 133 | run_test("test016.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 134 | run_test("test017.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 135 | run_test("test018.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 136 | run_test("test019.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 137 | run_test("test020.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 138 | run_test("test021.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 139 | else 140 | run_test(TEST_CASE,clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); 141 | end if; 142 | 143 | report "ALL TESTS WERE COMPLETED SUCCESSFULLY"; 144 | finish<='1'; 145 | wait; 146 | end process; 147 | 148 | end architecture; 149 | -------------------------------------------------------------------------------- /tools/src/lxp32dump/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 by Alex I. Kuznetsov. 3 | * 4 | * Part of the LXP32 CPU IP core. 5 | * 6 | * Main translation unit for the LXP32 disassembler. 7 | */ 8 | 9 | #ifdef _MSC_VER 10 | #define _CRT_SECURE_NO_WARNINGS 11 | #endif 12 | 13 | #include "disassembler.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static void displayUsage(std::ostream &os,const char *program) { 25 | os< Base address (for comments only)"< Input format (bin, textio, dec, hex), default: autodetect"< Output file name, default: standard output"<(in.gcount()); 47 | in.clear(); 48 | in.seekg(0); 49 | 50 | Disassembler::Format fmt=Disassembler::Textio; 51 | 52 | for(std::size_t i=0;iclk_i, 144 | rst_i=>rst_i, 145 | ce_i=>mul_ce, 146 | op1_i=>op1_i, 147 | op2_i=>op2_i, 148 | ce_o=>mul_we, 149 | result_o=>mul_result 150 | ); 151 | end generate; 152 | 153 | gen_mul_opt: if MUL_ARCH="opt" generate 154 | mul_inst: entity work.lxp32_mul_opt(rtl) 155 | port map( 156 | clk_i=>clk_i, 157 | rst_i=>rst_i, 158 | ce_i=>mul_ce, 159 | op1_i=>op1_i, 160 | op2_i=>op2_i, 161 | ce_o=>mul_we, 162 | result_o=>mul_result 163 | ); 164 | end generate; 165 | 166 | gen_mul_seq: if MUL_ARCH="seq" generate 167 | mul_inst: entity work.lxp32_mul_seq(rtl) 168 | port map( 169 | clk_i=>clk_i, 170 | rst_i=>rst_i, 171 | ce_i=>mul_ce, 172 | op1_i=>op1_i, 173 | op2_i=>op2_i, 174 | ce_o=>mul_we, 175 | result_o=>mul_result 176 | ); 177 | end generate; 178 | 179 | -- Divider 180 | 181 | div_ce<=cmd_div_i and valid_i; 182 | 183 | gen_divider: if DIVIDER_EN generate 184 | divider_inst: entity work.lxp32_divider(rtl) 185 | port map( 186 | clk_i=>clk_i, 187 | rst_i=>rst_i, 188 | ce_i=>div_ce, 189 | op1_i=>op1_i, 190 | op2_i=>op2_i, 191 | signed_i=>cmd_signed_i, 192 | rem_i=>cmd_div_mod_i, 193 | ce_o=>div_we, 194 | result_o=>div_result 195 | ); 196 | end generate; 197 | 198 | gen_no_divider: if not DIVIDER_EN generate 199 | div_we<=div_ce; 200 | div_result<=(others=>'0'); 201 | end generate; 202 | 203 | -- Shifter 204 | 205 | shift_ce<=cmd_shift_i and valid_i; 206 | 207 | shifter_inst: entity work.lxp32_shifter(rtl) 208 | port map( 209 | clk_i=>clk_i, 210 | rst_i=>rst_i, 211 | ce_i=>shift_ce, 212 | d_i=>op1_i, 213 | s_i=>op2_i(4 downto 0), 214 | right_i=>cmd_shift_right_i, 215 | sig_i=>cmd_signed_i, 216 | ce_o=>shift_we, 217 | d_o=>shift_result 218 | ); 219 | 220 | -- Result multiplexer 221 | 222 | result_mux_gen: for i in result_mux'range generate 223 | result_mux(i)<=(adder_result(i) and adder_we) or 224 | (logic_result(i) and logic_we) or 225 | (mul_result(i) and mul_we) or 226 | (div_result(i) and div_we) or 227 | (shift_result(i) and shift_we); 228 | end generate; 229 | 230 | result_o<=result_mux; 231 | 232 | result_we<=adder_we or logic_we or mul_we or div_we or shift_we; 233 | we_o<=result_we; 234 | 235 | -- Pipeline control 236 | 237 | process (clk_i) is 238 | begin 239 | if rising_edge(clk_i) then 240 | if rst_i='1' or result_we='1' then 241 | busy<='0'; 242 | elsif shift_ce='1' or mul_ce='1' or div_ce='1' then 243 | busy<='1'; 244 | end if; 245 | end if; 246 | end process; 247 | 248 | busy_o<=busy; 249 | 250 | end architecture; 251 | -------------------------------------------------------------------------------- /misc/highlight/notepad++/LXP32Assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 00// 01 02 03/* 04*/ 9 | 10 | 0x 11 | A B C D E F a b c d e f 12 | 13 | 14 | 15 | 16 | @ , 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | add and call cjmpe cjmpne cjmpsg cjmpsge cjmpsl cjmpsle cjmpug cjmpuge cjmpul cjmpule divs divu hlt jmp iret lc lcs lsb lub lw mods modu mov mul neg nop not or ret sb sl srs sru sub sw xcall xor 28 | cr irp iv0 iv1 iv2 iv3 iv4 iv5 iv6 iv7 r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r34 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44 r45 r46 r47 r48 r49 r50 r51 r52 r53 r54 r55 r56 r57 r58 r59 r60 r61 r62 r63 r64 r65 r66 r67 r68 r69 r70 r71 r72 r73 r74 r75 r76 r77 r78 r79 r80 r81 r82 r83 r84 r85 r86 r87 r88 r89 r90 r91 r92 r93 r94 r95 r96 r97 r98 r99 r100 r101 r102 r103 r104 r105 r106 r107 r108 r109 r110 r111 r112 r113 r114 r115 r116 r117 r118 r119 r120 r121 r122 r123 r124 r125 r126 r127 r128 r129 r130 r131 r132 r133 r134 r135 r136 r137 r138 r139 r140 r141 r142 r143 r144 r145 r146 r147 r148 r149 r150 r151 r152 r153 r154 r155 r156 r157 r158 r159 r160 r161 r162 r163 r164 r165 r166 r167 r168 r169 r170 r171 r172 r173 r174 r175 r176 r177 r178 r179 r180 r181 r182 r183 r184 r185 r186 r187 r188 r189 r190 r191 r192 r193 r194 r195 r196 r197 r198 r199 r200 r201 r202 r203 r204 r205 r206 r207 r208 r209 r210 r211 r212 r213 r214 r215 r216 r217 r218 r219 r220 r221 r222 r223 r224 r225 r226 r227 r228 r229 r230 r231 r232 r233 r234 r235 r236 r237 r238 r239 r240 r241 r242 r243 r244 r245 r246 r247 r248 r249 r250 r251 r252 r253 r254 r255 rp sp 29 | #define #else #endif #error #export #ifdef #ifndef #import #include #message 30 | .align .byte .reserve .word 31 | 32 | 33 | 34 | 35 | 00" 01\ 02" 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /rtl/lxp32_execute.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Execution unit 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- The third stage of the LXP32 pipeline. 9 | --------------------------------------------------------------------- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | 14 | entity lxp32_execute is 15 | generic( 16 | DBUS_RMW: boolean; 17 | DIVIDER_EN: boolean; 18 | MUL_ARCH: string 19 | ); 20 | port( 21 | clk_i: in std_logic; 22 | rst_i: in std_logic; 23 | 24 | cmd_loadop3_i: in std_logic; 25 | cmd_signed_i: in std_logic; 26 | cmd_dbus_i: in std_logic; 27 | cmd_dbus_store_i: in std_logic; 28 | cmd_dbus_byte_i: in std_logic; 29 | cmd_addsub_i: in std_logic; 30 | cmd_mul_i: in std_logic; 31 | cmd_div_i: in std_logic; 32 | cmd_div_mod_i: in std_logic; 33 | cmd_cmp_i: in std_logic; 34 | cmd_jump_i: in std_logic; 35 | cmd_negate_op2_i: in std_logic; 36 | cmd_and_i: in std_logic; 37 | cmd_xor_i: in std_logic; 38 | cmd_shift_i: in std_logic; 39 | cmd_shift_right_i: in std_logic; 40 | 41 | jump_type_i: in std_logic_vector(3 downto 0); 42 | 43 | op1_i: in std_logic_vector(31 downto 0); 44 | op2_i: in std_logic_vector(31 downto 0); 45 | op3_i: in std_logic_vector(31 downto 0); 46 | dst_i: in std_logic_vector(7 downto 0); 47 | 48 | sp_waddr_o: out std_logic_vector(7 downto 0); 49 | sp_we_o: out std_logic; 50 | sp_wdata_o: out std_logic_vector(31 downto 0); 51 | 52 | valid_i: in std_logic; 53 | ready_o: out std_logic; 54 | 55 | dbus_cyc_o: out std_logic; 56 | dbus_stb_o: out std_logic; 57 | dbus_we_o: out std_logic; 58 | dbus_sel_o: out std_logic_vector(3 downto 0); 59 | dbus_ack_i: in std_logic; 60 | dbus_adr_o: out std_logic_vector(31 downto 2); 61 | dbus_dat_o: out std_logic_vector(31 downto 0); 62 | dbus_dat_i: in std_logic_vector(31 downto 0); 63 | 64 | jump_valid_o: out std_logic; 65 | jump_dst_o: out std_logic_vector(29 downto 0); 66 | jump_ready_i: in std_logic; 67 | 68 | interrupt_return_o: out std_logic 69 | ); 70 | end entity; 71 | 72 | architecture rtl of lxp32_execute is 73 | 74 | -- Pipeline control signals 75 | 76 | signal busy: std_logic; 77 | signal can_execute: std_logic; 78 | 79 | -- ALU signals 80 | 81 | signal alu_result: std_logic_vector(31 downto 0); 82 | signal alu_we: std_logic; 83 | signal alu_busy: std_logic; 84 | 85 | signal alu_cmp_eq: std_logic; 86 | signal alu_cmp_ug: std_logic; 87 | signal alu_cmp_sg: std_logic; 88 | 89 | -- OP3 loader signals 90 | 91 | signal loadop3_we: std_logic; 92 | 93 | -- Jump machine signals 94 | 95 | signal jump_condition: std_logic; 96 | signal jump_valid: std_logic:='0'; 97 | signal jump_dst: std_logic_vector(jump_dst_o'range); 98 | 99 | -- DBUS signals 100 | 101 | signal dbus_result: std_logic_vector(31 downto 0); 102 | signal dbus_busy: std_logic; 103 | signal dbus_we: std_logic; 104 | 105 | -- Result mux signals 106 | 107 | signal result_mux: std_logic_vector(31 downto 0); 108 | signal result_valid: std_logic; 109 | signal result_regaddr: std_logic_vector(7 downto 0); 110 | 111 | signal dst_reg: std_logic_vector(7 downto 0); 112 | 113 | -- Signals related to interrupt handling 114 | 115 | signal interrupt_return: std_logic:='0'; 116 | 117 | begin 118 | 119 | -- Pipeline control 120 | 121 | busy<=alu_busy or dbus_busy; 122 | ready_o<=not busy; 123 | can_execute<=valid_i and not busy; 124 | 125 | -- ALU 126 | 127 | alu_inst: entity work.lxp32_alu(rtl) 128 | generic map( 129 | DIVIDER_EN=>DIVIDER_EN, 130 | MUL_ARCH=>MUL_ARCH 131 | ) 132 | port map( 133 | clk_i=>clk_i, 134 | rst_i=>rst_i, 135 | 136 | valid_i=>can_execute, 137 | 138 | cmd_signed_i=>cmd_signed_i, 139 | cmd_addsub_i=>cmd_addsub_i, 140 | cmd_mul_i=>cmd_mul_i, 141 | cmd_div_i=>cmd_div_i, 142 | cmd_div_mod_i=>cmd_div_mod_i, 143 | cmd_cmp_i=>cmd_cmp_i, 144 | cmd_negate_op2_i=>cmd_negate_op2_i, 145 | cmd_and_i=>cmd_and_i, 146 | cmd_xor_i=>cmd_xor_i, 147 | cmd_shift_i=>cmd_shift_i, 148 | cmd_shift_right_i=>cmd_shift_right_i, 149 | 150 | op1_i=>op1_i, 151 | op2_i=>op2_i, 152 | 153 | result_o=>alu_result, 154 | 155 | cmp_eq_o=>alu_cmp_eq, 156 | cmp_ug_o=>alu_cmp_ug, 157 | cmp_sg_o=>alu_cmp_sg, 158 | 159 | we_o=>alu_we, 160 | busy_o=>alu_busy 161 | ); 162 | 163 | -- OP3 loader 164 | 165 | loadop3_we<=can_execute and cmd_loadop3_i; 166 | 167 | -- Jump logic 168 | 169 | jump_condition<=(not cmd_cmp_i) or (jump_type_i(3) and alu_cmp_eq) or 170 | (jump_type_i(2) and not alu_cmp_eq) or (jump_type_i(1) and alu_cmp_ug) or 171 | (jump_type_i(0) and alu_cmp_sg); 172 | 173 | process (clk_i) is 174 | begin 175 | if rising_edge(clk_i) then 176 | if rst_i='1' then 177 | jump_valid<='0'; 178 | interrupt_return<='0'; 179 | jump_dst<=(others=>'-'); 180 | else 181 | if jump_valid='0' then 182 | jump_dst<=op1_i(31 downto 2); 183 | if can_execute='1' and cmd_jump_i='1' and jump_condition='1' then 184 | jump_valid<='1'; 185 | interrupt_return<=op1_i(0); 186 | end if; 187 | elsif jump_ready_i='1' then 188 | jump_valid<='0'; 189 | interrupt_return<='0'; 190 | end if; 191 | end if; 192 | end if; 193 | end process; 194 | 195 | jump_valid_o<=jump_valid or (can_execute and cmd_jump_i and jump_condition); 196 | jump_dst_o<=jump_dst when jump_valid='1' else op1_i(31 downto 2); 197 | 198 | interrupt_return_o<=interrupt_return; 199 | 200 | -- DBUS access 201 | 202 | dbus_inst: entity work.lxp32_dbus(rtl) 203 | generic map( 204 | RMW=>DBUS_RMW 205 | ) 206 | port map( 207 | clk_i=>clk_i, 208 | rst_i=>rst_i, 209 | 210 | valid_i=>can_execute, 211 | 212 | cmd_dbus_i=>cmd_dbus_i, 213 | cmd_dbus_store_i=>cmd_dbus_store_i, 214 | cmd_dbus_byte_i=>cmd_dbus_byte_i, 215 | cmd_signed_i=>cmd_signed_i, 216 | addr_i=>op1_i, 217 | wdata_i=>op2_i, 218 | 219 | rdata_o=>dbus_result, 220 | busy_o=>dbus_busy, 221 | we_o=>dbus_we, 222 | 223 | dbus_cyc_o=>dbus_cyc_o, 224 | dbus_stb_o=>dbus_stb_o, 225 | dbus_we_o=>dbus_we_o, 226 | dbus_sel_o=>dbus_sel_o, 227 | dbus_ack_i=>dbus_ack_i, 228 | dbus_adr_o=>dbus_adr_o, 229 | dbus_dat_o=>dbus_dat_o, 230 | dbus_dat_i=>dbus_dat_i 231 | ); 232 | 233 | -- Result multiplexer 234 | 235 | result_mux_gen: for i in result_mux'range generate 236 | result_mux(i)<=(alu_result(i) and alu_we) or 237 | (op3_i(i) and loadop3_we) or 238 | (dbus_result(i) and dbus_we); 239 | end generate; 240 | 241 | result_valid<=alu_we or loadop3_we or dbus_we; 242 | 243 | -- Write destination register 244 | 245 | process (clk_i) is 246 | begin 247 | if rising_edge(clk_i) then 248 | if can_execute='1' then 249 | dst_reg<=dst_i; 250 | end if; 251 | end if; 252 | end process; 253 | 254 | result_regaddr<=dst_i when can_execute='1' else dst_reg; 255 | 256 | sp_we_o<=result_valid; 257 | sp_waddr_o<=result_regaddr; 258 | sp_wdata_o<=result_mux; 259 | 260 | end architecture; 261 | -------------------------------------------------------------------------------- /rtl/lxp32_fetch.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Instruction fetch 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | -- 8 | -- The first stage of the LXP32 pipeline. 9 | --------------------------------------------------------------------- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; 14 | 15 | entity lxp32_fetch is 16 | generic( 17 | START_ADDR: std_logic_vector(31 downto 0) 18 | ); 19 | port( 20 | clk_i: in std_logic; 21 | rst_i: in std_logic; 22 | 23 | lli_re_o: out std_logic; 24 | lli_adr_o: out std_logic_vector(29 downto 0); 25 | lli_dat_i: in std_logic_vector(31 downto 0); 26 | lli_busy_i: in std_logic; 27 | 28 | word_o: out std_logic_vector(31 downto 0); 29 | current_ip_o: out std_logic_vector(29 downto 0); 30 | next_ip_o: out std_logic_vector(29 downto 0); 31 | valid_o: out std_logic; 32 | ready_i: in std_logic; 33 | 34 | jump_valid_i: in std_logic; 35 | jump_dst_i: in std_logic_vector(29 downto 0); 36 | jump_ready_o: out std_logic 37 | ); 38 | end entity; 39 | 40 | architecture rtl of lxp32_fetch is 41 | 42 | signal init: std_logic:='1'; 43 | signal init_cnt: unsigned(7 downto 0):=(others=>'0'); 44 | 45 | signal fetch_addr: std_logic_vector(29 downto 0):=START_ADDR(31 downto 2); 46 | 47 | signal next_word: std_logic; 48 | signal suppress_re: std_logic:='0'; 49 | signal re: std_logic; 50 | signal requested: std_logic:='0'; 51 | 52 | signal fifo_rst: std_logic; 53 | signal fifo_we: std_logic; 54 | signal fifo_din: std_logic_vector(31 downto 0); 55 | signal fifo_re: std_logic; 56 | signal fifo_dout: std_logic_vector(31 downto 0); 57 | signal fifo_empty: std_logic; 58 | signal fifo_full: std_logic; 59 | 60 | signal jr: std_logic:='0'; 61 | 62 | signal next_ip: std_logic_vector(fetch_addr'range); 63 | signal current_ip: std_logic_vector(fetch_addr'range); 64 | 65 | begin 66 | 67 | -- INIT state machine (to initialize all registers) 68 | 69 | -- All CPU registers are expected to be zero-initialized after reset. 70 | -- Since these registers are implemented as a RAM block, we perform 71 | -- the initialization sequentially by generating "mov rN, 0" instructions 72 | -- for each N from 0 to 255. 73 | -- 74 | -- With SRAM-based FPGAs, flip-flops and RAM blocks have deterministic 75 | -- state after configuration. On these technologies the CPU can operate 76 | -- without reset and the initialization procedure described above is not 77 | -- needed. However, the initialization is still performed as usual when 78 | -- external reset signal is asserted. 79 | 80 | process (clk_i) is 81 | begin 82 | if rising_edge(clk_i) then 83 | if rst_i='1' then 84 | init<='0'; 85 | init_cnt<=(others=>'0'); 86 | else 87 | if init='0' and ready_i='1' then 88 | init_cnt<=init_cnt+1; 89 | if init_cnt=X"FF" then 90 | init<='1'; 91 | end if; 92 | end if; 93 | end if; 94 | end if; 95 | end process; 96 | 97 | -- FETCH state machine 98 | 99 | process (clk_i) is 100 | begin 101 | if rising_edge(clk_i) then 102 | if rst_i='1' then 103 | fetch_addr<=START_ADDR(31 downto 2); 104 | requested<='0'; 105 | jr<='0'; 106 | suppress_re<='0'; 107 | next_ip<=(others=>'-'); 108 | else 109 | jr<='0'; 110 | -- Suppress LLI request if jump signal is active but will not be processed 111 | -- in this cycle. Helps to reduce jump latency with high-latency LLI slaves. 112 | -- Note: gating "re" with "jump_valid_i and not jr" asynchronously would 113 | -- reduce jump latency even more, but we really want to avoid too large 114 | -- clock-to-out on LLI outputs. 115 | suppress_re<=jump_valid_i and not jr and not next_word; 116 | if lli_busy_i='0' then 117 | requested<=re and not (jump_valid_i and not jr); 118 | end if; 119 | if next_word='1' then 120 | -- It's not immediately obvious why, but current_ip and next_ip will contain 121 | -- the addresses of the current instruction and the next instruction to be 122 | -- fetched, respectively, by the time the instruction is passed to the decode 123 | -- stage. Basically, this is because when either the decoder or the IBUS 124 | -- stalls, the fetch_addr counter will also stop incrementing. 125 | next_ip<=fetch_addr; 126 | current_ip<=next_ip; 127 | if jump_valid_i='1' and jr='0' then 128 | fetch_addr<=jump_dst_i; 129 | jr<='1'; 130 | else 131 | fetch_addr<=std_logic_vector(unsigned(fetch_addr)+1); 132 | end if; 133 | end if; 134 | end if; 135 | end if; 136 | end process; 137 | 138 | next_word<=(fifo_empty or ready_i) and not lli_busy_i and init; 139 | re<=(fifo_empty or ready_i) and init and not suppress_re; 140 | lli_re_o<=re; 141 | lli_adr_o<=fetch_addr; 142 | 143 | jump_ready_o<=jr; 144 | 145 | -- Small instruction buffer 146 | 147 | fifo_rst<=rst_i or (jump_valid_i and not jr); 148 | fifo_we<=requested and not lli_busy_i; 149 | fifo_din<=lli_dat_i; 150 | fifo_re<=ready_i and not fifo_empty; 151 | 152 | ubuf_inst: entity work.lxp32_ubuf(rtl) 153 | generic map( 154 | DATA_WIDTH=>32 155 | ) 156 | port map( 157 | clk_i=>clk_i, 158 | rst_i=>fifo_rst, 159 | 160 | we_i=>fifo_we, 161 | d_i=>fifo_din, 162 | re_i=>fifo_re, 163 | d_o=>fifo_dout, 164 | 165 | empty_o=>fifo_empty, 166 | full_o=>fifo_full 167 | ); 168 | 169 | next_ip_o<=next_ip; 170 | current_ip_o<=current_ip; 171 | word_o<=fifo_dout when init='1' else X"40"&std_logic_vector(init_cnt)&X"0000"; 172 | valid_o<=not fifo_empty or not init; 173 | 174 | -- Note: the following code contains a few simulation-only assertions 175 | -- to check that current_ip and next_ip signals, used in procedure calls 176 | -- and interrupts, are correct. 177 | -- This code should be ignored by a synthesizer since it doesn't drive 178 | -- any signals, but we also surround it by metacomments, just in case. 179 | 180 | -- synthesis translate_off 181 | 182 | process (clk_i) is 183 | type Pair is record 184 | addr: std_logic_vector(fetch_addr'range); 185 | data: std_logic_vector(31 downto 0); 186 | end record; 187 | type Pairs is array (7 downto 0) of Pair; 188 | variable buf: Pairs; 189 | variable count: integer range buf'range:=0; 190 | variable current_pair: Pair; 191 | begin 192 | if rising_edge(clk_i) then 193 | if fifo_rst='1' then -- jump 194 | count:=0; 195 | elsif fifo_we='1' then -- LLI returned data 196 | current_pair.data:=fifo_din; 197 | buf(count):=current_pair; 198 | count:=count+1; 199 | end if; 200 | if re='1' and lli_busy_i='0' then -- data requested 201 | current_pair.addr:=fetch_addr; 202 | end if; 203 | if fifo_empty='0' and fifo_rst='0' then -- fetch output is valid 204 | assert count>0 205 | report "Fetch: buffer should be empty" 206 | severity failure; 207 | assert buf(0).data=fifo_dout 208 | report "Fetch: incorrect data" 209 | severity failure; 210 | assert buf(0).addr=current_ip 211 | report "Fetch: incorrect current_ip" 212 | severity failure; 213 | assert std_logic_vector(unsigned(buf(0).addr)+1)=next_ip 214 | report "Fetch: incorrect next_ip" 215 | severity failure; 216 | if ready_i='1' then 217 | buf(buf'high-1 downto 0):=buf(buf'high downto 1); -- we don't care about the highest item 218 | count:=count-1; 219 | end if; 220 | end if; 221 | end if; 222 | end process; 223 | 224 | -- synthesis translate_on 225 | 226 | end architecture; 227 | -------------------------------------------------------------------------------- /verify/lxp32/src/platform/intercon.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- Simple WISHBONE interconnect 3 | -- 4 | -- Generated by wigen at Sat Nov 2 11:45:49 2024 5 | -- 6 | -- Configuration: 7 | -- Number of masters: 2 8 | -- Number of slaves: 5 9 | -- Master address width: 32 10 | -- Slave address width: 28 11 | -- Port size: 32 12 | -- Port granularity: 8 13 | -- Entity name: intercon 14 | -- Pipelined arbiter: no 15 | -- Registered feedback: no 16 | -- Unsafe slave decoder: no 17 | -- 18 | -- Command line: 19 | -- wigen -e intercon 2 5 32 28 32 8 20 | --------------------------------------------------------------------- 21 | 22 | library ieee; 23 | use ieee.std_logic_1164.all; 24 | 25 | entity intercon is 26 | port( 27 | clk_i: in std_logic; 28 | rst_i: in std_logic; 29 | 30 | s0_cyc_i: in std_logic; 31 | s0_stb_i: in std_logic; 32 | s0_we_i: in std_logic; 33 | s0_sel_i: in std_logic_vector(3 downto 0); 34 | s0_ack_o: out std_logic; 35 | s0_adr_i: in std_logic_vector(31 downto 2); 36 | s0_dat_i: in std_logic_vector(31 downto 0); 37 | s0_dat_o: out std_logic_vector(31 downto 0); 38 | 39 | s1_cyc_i: in std_logic; 40 | s1_stb_i: in std_logic; 41 | s1_we_i: in std_logic; 42 | s1_sel_i: in std_logic_vector(3 downto 0); 43 | s1_ack_o: out std_logic; 44 | s1_adr_i: in std_logic_vector(31 downto 2); 45 | s1_dat_i: in std_logic_vector(31 downto 0); 46 | s1_dat_o: out std_logic_vector(31 downto 0); 47 | 48 | m0_cyc_o: out std_logic; 49 | m0_stb_o: out std_logic; 50 | m0_we_o: out std_logic; 51 | m0_sel_o: out std_logic_vector(3 downto 0); 52 | m0_ack_i: in std_logic; 53 | m0_adr_o: out std_logic_vector(27 downto 2); 54 | m0_dat_o: out std_logic_vector(31 downto 0); 55 | m0_dat_i: in std_logic_vector(31 downto 0); 56 | 57 | m1_cyc_o: out std_logic; 58 | m1_stb_o: out std_logic; 59 | m1_we_o: out std_logic; 60 | m1_sel_o: out std_logic_vector(3 downto 0); 61 | m1_ack_i: in std_logic; 62 | m1_adr_o: out std_logic_vector(27 downto 2); 63 | m1_dat_o: out std_logic_vector(31 downto 0); 64 | m1_dat_i: in std_logic_vector(31 downto 0); 65 | 66 | m2_cyc_o: out std_logic; 67 | m2_stb_o: out std_logic; 68 | m2_we_o: out std_logic; 69 | m2_sel_o: out std_logic_vector(3 downto 0); 70 | m2_ack_i: in std_logic; 71 | m2_adr_o: out std_logic_vector(27 downto 2); 72 | m2_dat_o: out std_logic_vector(31 downto 0); 73 | m2_dat_i: in std_logic_vector(31 downto 0); 74 | 75 | m3_cyc_o: out std_logic; 76 | m3_stb_o: out std_logic; 77 | m3_we_o: out std_logic; 78 | m3_sel_o: out std_logic_vector(3 downto 0); 79 | m3_ack_i: in std_logic; 80 | m3_adr_o: out std_logic_vector(27 downto 2); 81 | m3_dat_o: out std_logic_vector(31 downto 0); 82 | m3_dat_i: in std_logic_vector(31 downto 0); 83 | 84 | m4_cyc_o: out std_logic; 85 | m4_stb_o: out std_logic; 86 | m4_we_o: out std_logic; 87 | m4_sel_o: out std_logic_vector(3 downto 0); 88 | m4_ack_i: in std_logic; 89 | m4_adr_o: out std_logic_vector(27 downto 2); 90 | m4_dat_o: out std_logic_vector(31 downto 0); 91 | m4_dat_i: in std_logic_vector(31 downto 0) 92 | ); 93 | end entity; 94 | 95 | architecture rtl of intercon is 96 | 97 | signal request: std_logic_vector(1 downto 0); 98 | signal grant_next: std_logic_vector(1 downto 0); 99 | signal grant: std_logic_vector(1 downto 0); 100 | signal grant_reg: std_logic_vector(1 downto 0):=(others=>'0'); 101 | 102 | signal select_slave: std_logic_vector(5 downto 0); 103 | 104 | signal cyc_mux: std_logic; 105 | signal stb_mux: std_logic; 106 | signal we_mux: std_logic; 107 | signal sel_mux: std_logic_vector(3 downto 0); 108 | signal adr_mux: std_logic_vector(31 downto 2); 109 | signal wdata_mux: std_logic_vector(31 downto 0); 110 | 111 | signal ack_mux: std_logic; 112 | signal rdata_mux: std_logic_vector(31 downto 0); 113 | 114 | begin 115 | 116 | -- ARBITER 117 | -- Selects the active master. Masters with lower port numbers 118 | -- have higher priority. Ongoing cycles are not interrupted. 119 | 120 | request<=s1_cyc_i&s0_cyc_i; 121 | 122 | grant_next<="01" when request(0)='1' else 123 | "10" when request(1)='1' else 124 | (others=>'0'); 125 | 126 | grant<=grant_reg when (request and grant_reg)/="00" else grant_next; 127 | 128 | process (clk_i) is 129 | begin 130 | if rising_edge(clk_i) then 131 | if rst_i='1' then 132 | grant_reg<=(others=>'0'); 133 | else 134 | grant_reg<=grant; 135 | end if; 136 | end if; 137 | end process; 138 | 139 | -- MASTER->SLAVE MUX 140 | 141 | cyc_mux<=(s0_cyc_i and grant(0)) or 142 | (s1_cyc_i and grant(1)); 143 | 144 | stb_mux<=(s0_stb_i and grant(0)) or 145 | (s1_stb_i and grant(1)); 146 | 147 | we_mux<=(s0_we_i and grant(0)) or 148 | (s1_we_i and grant(1)); 149 | 150 | sel_mux_gen: for i in sel_mux'range generate 151 | sel_mux(i)<=(s0_sel_i(i) and grant(0)) or 152 | (s1_sel_i(i) and grant(1)); 153 | end generate; 154 | 155 | adr_mux_gen: for i in adr_mux'range generate 156 | adr_mux(i)<=(s0_adr_i(i) and grant(0)) or 157 | (s1_adr_i(i) and grant(1)); 158 | end generate; 159 | 160 | wdata_mux_gen: for i in wdata_mux'range generate 161 | wdata_mux(i)<=(s0_dat_i(i) and grant(0)) or 162 | (s1_dat_i(i) and grant(1)); 163 | end generate; 164 | 165 | -- MASTER->SLAVE DEMUX 166 | 167 | select_slave<="000001" when adr_mux(31 downto 28)="0000" else 168 | "000010" when adr_mux(31 downto 28)="0001" else 169 | "000100" when adr_mux(31 downto 28)="0010" else 170 | "001000" when adr_mux(31 downto 28)="0011" else 171 | "010000" when adr_mux(31 downto 28)="0100" else 172 | "100000"; -- fallback slave 173 | 174 | m0_cyc_o<=cyc_mux and select_slave(0); 175 | m0_stb_o<=stb_mux and select_slave(0); 176 | m0_we_o<=we_mux; 177 | m0_sel_o<=sel_mux; 178 | m0_adr_o<=adr_mux(m0_adr_o'range); 179 | m0_dat_o<=wdata_mux; 180 | 181 | m1_cyc_o<=cyc_mux and select_slave(1); 182 | m1_stb_o<=stb_mux and select_slave(1); 183 | m1_we_o<=we_mux; 184 | m1_sel_o<=sel_mux; 185 | m1_adr_o<=adr_mux(m1_adr_o'range); 186 | m1_dat_o<=wdata_mux; 187 | 188 | m2_cyc_o<=cyc_mux and select_slave(2); 189 | m2_stb_o<=stb_mux and select_slave(2); 190 | m2_we_o<=we_mux; 191 | m2_sel_o<=sel_mux; 192 | m2_adr_o<=adr_mux(m2_adr_o'range); 193 | m2_dat_o<=wdata_mux; 194 | 195 | m3_cyc_o<=cyc_mux and select_slave(3); 196 | m3_stb_o<=stb_mux and select_slave(3); 197 | m3_we_o<=we_mux; 198 | m3_sel_o<=sel_mux; 199 | m3_adr_o<=adr_mux(m3_adr_o'range); 200 | m3_dat_o<=wdata_mux; 201 | 202 | m4_cyc_o<=cyc_mux and select_slave(4); 203 | m4_stb_o<=stb_mux and select_slave(4); 204 | m4_we_o<=we_mux; 205 | m4_sel_o<=sel_mux; 206 | m4_adr_o<=adr_mux(m4_adr_o'range); 207 | m4_dat_o<=wdata_mux; 208 | 209 | -- SLAVE->MASTER MUX 210 | 211 | ack_mux<=(m0_ack_i and select_slave(0)) or 212 | (m1_ack_i and select_slave(1)) or 213 | (m2_ack_i and select_slave(2)) or 214 | (m3_ack_i and select_slave(3)) or 215 | (m4_ack_i and select_slave(4)) or 216 | (cyc_mux and stb_mux and select_slave(5)); -- fallback slave 217 | 218 | rdata_mux_gen: for i in rdata_mux'range generate 219 | rdata_mux(i)<=(m0_dat_i(i) and select_slave(0)) or 220 | (m1_dat_i(i) and select_slave(1)) or 221 | (m2_dat_i(i) and select_slave(2)) or 222 | (m3_dat_i(i) and select_slave(3)) or 223 | (m4_dat_i(i) and select_slave(4)); 224 | end generate; 225 | 226 | -- SLAVE->MASTER DEMUX 227 | 228 | s0_ack_o<=ack_mux and grant(0); 229 | s0_dat_o<=rdata_mux; 230 | 231 | s1_ack_o<=ack_mux and grant(1); 232 | s1_dat_o<=rdata_mux; 233 | 234 | end architecture; 235 | -------------------------------------------------------------------------------- /rtl/lxp32_cpu.vhd: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------- 2 | -- LXP32 CPU Core 3 | -- 4 | -- Part of the LXP32 CPU 5 | -- 6 | -- Copyright (c) 2016 by Alex I. Kuznetsov 7 | --------------------------------------------------------------------- 8 | 9 | library ieee; 10 | use ieee.std_logic_1164.all; 11 | 12 | entity lxp32_cpu is 13 | generic( 14 | DBUS_RMW: boolean; 15 | DIVIDER_EN: boolean; 16 | MUL_ARCH: string; 17 | START_ADDR: std_logic_vector(31 downto 0) 18 | ); 19 | port( 20 | clk_i: in std_logic; 21 | rst_i: in std_logic; 22 | 23 | lli_re_o: out std_logic; 24 | lli_adr_o: out std_logic_vector(29 downto 0); 25 | lli_dat_i: in std_logic_vector(31 downto 0); 26 | lli_busy_i: in std_logic; 27 | 28 | dbus_cyc_o: out std_logic; 29 | dbus_stb_o: out std_logic; 30 | dbus_we_o: out std_logic; 31 | dbus_sel_o: out std_logic_vector(3 downto 0); 32 | dbus_ack_i: in std_logic; 33 | dbus_adr_o: out std_logic_vector(31 downto 2); 34 | dbus_dat_o: out std_logic_vector(31 downto 0); 35 | dbus_dat_i: in std_logic_vector(31 downto 0); 36 | 37 | irq_i: in std_logic_vector(7 downto 0) 38 | ); 39 | end entity; 40 | 41 | architecture rtl of lxp32_cpu is 42 | 43 | signal fetch_word: std_logic_vector(31 downto 0); 44 | signal fetch_next_ip: std_logic_vector(29 downto 0); 45 | signal fetch_current_ip: std_logic_vector(29 downto 0); 46 | signal fetch_valid: std_logic; 47 | signal fetch_jump_ready: std_logic; 48 | 49 | signal decode_ready: std_logic; 50 | signal decode_valid: std_logic; 51 | 52 | signal decode_cmd_loadop3: std_logic; 53 | signal decode_cmd_signed: std_logic; 54 | signal decode_cmd_dbus: std_logic; 55 | signal decode_cmd_dbus_store: std_logic; 56 | signal decode_cmd_dbus_byte: std_logic; 57 | signal decode_cmd_addsub: std_logic; 58 | signal decode_cmd_mul: std_logic; 59 | signal decode_cmd_div: std_logic; 60 | signal decode_cmd_div_mod: std_logic; 61 | signal decode_cmd_cmp: std_logic; 62 | signal decode_cmd_jump: std_logic; 63 | signal decode_cmd_negate_op2: std_logic; 64 | signal decode_cmd_and: std_logic; 65 | signal decode_cmd_xor: std_logic; 66 | signal decode_cmd_shift: std_logic; 67 | signal decode_cmd_shift_right: std_logic; 68 | 69 | signal decode_jump_type: std_logic_vector(3 downto 0); 70 | 71 | signal decode_op1: std_logic_vector(31 downto 0); 72 | signal decode_op2: std_logic_vector(31 downto 0); 73 | signal decode_op3: std_logic_vector(31 downto 0); 74 | signal decode_dst: std_logic_vector(7 downto 0); 75 | 76 | signal execute_ready: std_logic; 77 | signal execute_jump_valid: std_logic; 78 | signal execute_jump_dst: std_logic_vector(29 downto 0); 79 | 80 | signal sp_raddr1: std_logic_vector(7 downto 0); 81 | signal sp_rdata1: std_logic_vector(31 downto 0); 82 | signal sp_raddr2: std_logic_vector(7 downto 0); 83 | signal sp_rdata2: std_logic_vector(31 downto 0); 84 | signal sp_waddr: std_logic_vector(7 downto 0); 85 | signal sp_we: std_logic; 86 | signal sp_wdata: std_logic_vector(31 downto 0); 87 | 88 | signal interrupt_valid: std_logic; 89 | signal interrupt_vector: std_logic_vector(2 downto 0); 90 | signal interrupt_ready: std_logic; 91 | signal interrupt_return: std_logic; 92 | signal interrupt_wakeup: std_logic; 93 | 94 | begin 95 | 96 | fetch_inst: entity work.lxp32_fetch(rtl) 97 | generic map( 98 | START_ADDR=>START_ADDR 99 | ) 100 | port map( 101 | clk_i=>clk_i, 102 | rst_i=>rst_i, 103 | 104 | lli_re_o=>lli_re_o, 105 | lli_adr_o=>lli_adr_o, 106 | lli_dat_i=>lli_dat_i, 107 | lli_busy_i=>lli_busy_i, 108 | 109 | word_o=>fetch_word, 110 | next_ip_o=>fetch_next_ip, 111 | current_ip_o=>fetch_current_ip, 112 | valid_o=>fetch_valid, 113 | ready_i=>decode_ready, 114 | 115 | jump_valid_i=>execute_jump_valid, 116 | jump_dst_i=>execute_jump_dst, 117 | jump_ready_o=>fetch_jump_ready 118 | ); 119 | 120 | decode_inst: entity work.lxp32_decode(rtl) 121 | port map( 122 | clk_i=>clk_i, 123 | rst_i=>rst_i, 124 | 125 | word_i=>fetch_word, 126 | next_ip_i=>fetch_next_ip, 127 | current_ip_i=>fetch_current_ip, 128 | valid_i=>fetch_valid, 129 | jump_valid_i=>execute_jump_valid, 130 | ready_o=>decode_ready, 131 | 132 | interrupt_valid_i=>interrupt_valid, 133 | interrupt_vector_i=>interrupt_vector, 134 | interrupt_ready_o=>interrupt_ready, 135 | 136 | wakeup_i=>interrupt_wakeup, 137 | 138 | sp_raddr1_o=>sp_raddr1, 139 | sp_rdata1_i=>sp_rdata1, 140 | sp_raddr2_o=>sp_raddr2, 141 | sp_rdata2_i=>sp_rdata2, 142 | 143 | ready_i=>execute_ready, 144 | valid_o=>decode_valid, 145 | 146 | cmd_loadop3_o=>decode_cmd_loadop3, 147 | cmd_signed_o=>decode_cmd_signed, 148 | cmd_dbus_o=>decode_cmd_dbus, 149 | cmd_dbus_store_o=>decode_cmd_dbus_store, 150 | cmd_dbus_byte_o=>decode_cmd_dbus_byte, 151 | cmd_addsub_o=>decode_cmd_addsub, 152 | cmd_mul_o=>decode_cmd_mul, 153 | cmd_div_o=>decode_cmd_div, 154 | cmd_div_mod_o=>decode_cmd_div_mod, 155 | cmd_cmp_o=>decode_cmd_cmp, 156 | cmd_jump_o=>decode_cmd_jump, 157 | cmd_negate_op2_o=>decode_cmd_negate_op2, 158 | cmd_and_o=>decode_cmd_and, 159 | cmd_xor_o=>decode_cmd_xor, 160 | cmd_shift_o=>decode_cmd_shift, 161 | cmd_shift_right_o=>decode_cmd_shift_right, 162 | 163 | jump_type_o=>decode_jump_type, 164 | 165 | op1_o=>decode_op1, 166 | op2_o=>decode_op2, 167 | op3_o=>decode_op3, 168 | dst_o=>decode_dst 169 | ); 170 | 171 | execute_inst: entity work.lxp32_execute(rtl) 172 | generic map( 173 | DBUS_RMW=>DBUS_RMW, 174 | DIVIDER_EN=>DIVIDER_EN, 175 | MUL_ARCH=>MUL_ARCH 176 | ) 177 | port map( 178 | clk_i=>clk_i, 179 | rst_i=>rst_i, 180 | 181 | cmd_loadop3_i=>decode_cmd_loadop3, 182 | cmd_signed_i=>decode_cmd_signed, 183 | cmd_dbus_i=>decode_cmd_dbus, 184 | cmd_dbus_store_i=>decode_cmd_dbus_store, 185 | cmd_dbus_byte_i=>decode_cmd_dbus_byte, 186 | cmd_addsub_i=>decode_cmd_addsub, 187 | cmd_mul_i=>decode_cmd_mul, 188 | cmd_div_i=>decode_cmd_div, 189 | cmd_div_mod_i=>decode_cmd_div_mod, 190 | cmd_cmp_i=>decode_cmd_cmp, 191 | cmd_jump_i=>decode_cmd_jump, 192 | cmd_negate_op2_i=>decode_cmd_negate_op2, 193 | cmd_and_i=>decode_cmd_and, 194 | cmd_xor_i=>decode_cmd_xor, 195 | cmd_shift_i=>decode_cmd_shift, 196 | cmd_shift_right_i=>decode_cmd_shift_right, 197 | 198 | jump_type_i=>decode_jump_type, 199 | 200 | op1_i=>decode_op1, 201 | op2_i=>decode_op2, 202 | op3_i=>decode_op3, 203 | dst_i=>decode_dst, 204 | 205 | sp_waddr_o=>sp_waddr, 206 | sp_we_o=>sp_we, 207 | sp_wdata_o=>sp_wdata, 208 | 209 | valid_i=>decode_valid, 210 | ready_o=>execute_ready, 211 | 212 | dbus_cyc_o=>dbus_cyc_o, 213 | dbus_stb_o=>dbus_stb_o, 214 | dbus_we_o=>dbus_we_o, 215 | dbus_sel_o=>dbus_sel_o, 216 | dbus_ack_i=>dbus_ack_i, 217 | dbus_adr_o=>dbus_adr_o, 218 | dbus_dat_o=>dbus_dat_o, 219 | dbus_dat_i=>dbus_dat_i, 220 | 221 | jump_valid_o=>execute_jump_valid, 222 | jump_dst_o=>execute_jump_dst, 223 | jump_ready_i=>fetch_jump_ready, 224 | 225 | interrupt_return_o=>interrupt_return 226 | ); 227 | 228 | scratchpad_inst: entity work.lxp32_scratchpad(rtl) 229 | port map( 230 | clk_i=>clk_i, 231 | 232 | raddr1_i=>sp_raddr1, 233 | rdata1_o=>sp_rdata1, 234 | raddr2_i=>sp_raddr2, 235 | rdata2_o=>sp_rdata2, 236 | 237 | waddr_i=>sp_waddr, 238 | we_i=>sp_we, 239 | wdata_i=>sp_wdata 240 | ); 241 | 242 | interrupt_mux_inst: entity work.lxp32_interrupt_mux(rtl) 243 | port map( 244 | clk_i=>clk_i, 245 | rst_i=>rst_i, 246 | 247 | irq_i=>irq_i, 248 | 249 | interrupt_valid_o=>interrupt_valid, 250 | interrupt_vector_o=>interrupt_vector, 251 | interrupt_ready_i=>interrupt_ready, 252 | interrupt_return_i=>interrupt_return, 253 | 254 | wakeup_o=>interrupt_wakeup, 255 | 256 | sp_waddr_i=>sp_waddr, 257 | sp_we_i=>sp_we, 258 | sp_wdata_i=>sp_wdata 259 | ); 260 | 261 | end architecture; 262 | --------------------------------------------------------------------------------