URL
https://opencores.org/ocsvn/lxp32/lxp32/trunk
Subversion Repositories lxp32
Compare Revisions
- This comparison shows the changes necessary to convert path
/lxp32/trunk
- from Rev 9 to Rev 12
- ↔ Reverse comparison
Rev 9 → Rev 12
/doc/lxp32-trm.pdf
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/doc/src/trm/frontmatter.tex
15,7 → 15,7
\Large a lightweight open source 32-bit CPU core\par |
\LARGE \textbf{Technical Reference Manual}\par |
\vspace{1.2\onelineskip} |
\large Version 1.2\par |
\large Version 1.3\par |
\vspace*{4\onelineskip} |
\end{center} |
\vspace*{\fill} |
34,7 → 34,7
|
\vspace*{\fill} |
|
Copyright \textcopyright{} 2016--2019 by Alex I. Kuznetsov. |
Copyright \textcopyright{} 2016--2022 by Alex I. Kuznetsov. |
|
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: |
|
/doc/src/trm/lxp32-trm.tex
231,7 → 231,7
|
\subsection{Control register} |
|
\lxp{} supports 8 interrupts with hardwired priority levels (interrupts with lower vector numbers have higher priority). Interrupts vectors (pointers to interrupt handlers) are stored in the \code{iv0}--\code{iv7} registers. Interrupt handling is controlled by the \code{cr} register (Table \ref{tab:cr}). |
\lxp{} supports 8 interrupts with hardwired priority levels (interrupts with lower vector numbers have higher priority). Interrupt vectors (pointers to interrupt handlers) are stored in the \code{iv0}--\code{iv7} registers. Interrupt handling is controlled by the \code{cr} register (Table \ref{tab:cr}). |
|
\begin{table}[htbp] |
\caption{Control register} |
241,20 → 241,20
Bit & Description \\ |
\midrule |
0 & Enable interrupt 0 \\ |
1 & Enable interrupt 1 \\ |
& \ldots \\ |
7 & Enable interrupt 7 \\ |
8 & Temporarily block interrupt 0 \\ |
9 & Temporarily block interrupt 1 \\ |
8 & Interrupt 0 wake-up flag \\ |
& \ldots \\ |
15 & Temporarily block interrupt 7 \\ |
15 & Interrupt 7 wake-up flag \\ |
31--16 & \emph{Reserved} \\ |
\bottomrule |
\end{tabularx} |
\end{table} |
|
Disabled interrupts are ignored altogether: if the CPU receives an interrupt request signal while the corresponding interrupt is disabled, the interrupt handler will not be called even if the interrupt is enabled later. Conversely, temporarily blocked interrupts are still registered, but their handlers are not called until they are unblocked. |
Disabled interrupts are ignored altogether: if the CPU receives an interrupt request signal while the corresponding interrupt is disabled, the interrupt will not be processed even if it is enabled later. |
|
Wake-up flag marks the interrupt as a \emph{wake-up interrupt} (see below). |
|
Like other registers, \code{cr} is zero-initialized during the CPU reset, meaning that no interrupts are initially enabled. |
|
\subsection{Invoking interrupt handlers} |
263,6 → 263,12
|
An interrupt handler returns using the \code{\instr{jmp} irp} instruction which also has an \instr{iret} alias. Until the interrupt handler returns, the CPU will defer further interrupt processing (although incoming interrupt requests will still be registered). This also means that the \code{irp} register value will not be unexpectedly overwritten. When executing the \code{\instr{jmp} irp} instruction, the CPU will recognize the \code{IRF} flag and resume interrupt processing as usual. It is also possible to perform a conditional return from the interrupt handler, similarly to the technique described in Section \ref{sec:callingprocedures} for conditional procedure returns. |
|
\subsection{Wake-up interrupts} |
|
When a wake-up interrupt is received, the interrupt handler is not called, but the CPU still resumes execution if halted by the \instr{hlt} instruction. The effect is similar to invoking an interrupt with an empty handler (containing only \instr{iret}), but without the overhead of interrupt processing. Wake-up interrupts do not affect the CPU when it is not halted. |
|
Unlike normal interrupts, wake-up interrupts are processed even when the CPU executes an interrupt handler for another interrupt. |
|
\subsection{Non-returnable interrupts} |
|
If an interrupt vector has the least significant bit (\code{IRF}) set, the CPU will resume interrupt processing immediately. One should not try to invoke \instr{iret} from such a handler since the \code{irp} register could have been overwritten by another interrupt. This technique can be useful when the CPU's only task is to process external events: |
494,6 → 500,7
\end{figure} |
|
\section{Interrupts} |
\label{sec:interrupts} |
|
\lxp{} registers an interrupt condition when the corresponding request signal goes from \code{0} to \code{1}. Transitions from \code{1} to \code{0} are ignored. All interrupt request signals must be synchronous with the system clock (\signal{clk\_i}); if coming from an asynchronous source, they must be synchronized using a sequence of at least two flip-flops clocked by \signal{clk\_i}. These flip-flops are not included in the \lxp{} core in order not to increase interrupt processing delay for interrupt sources that are inherently synchronous. Failure to properly synchronize interrupt request signals will cause timing violations that will manifest itself as intermittent, hard to debug faults. |
|
1105,7 → 1112,7
\subsection{\instr{hlt} -- Halt} |
\label{subsec:instr:hlt} |
|
Wait for an interrupt. |
Halt the CPU until an enabled interrupt is received. |
|
\subsubsection{Syntax} |
|
1828,6 → 1835,10
|
\chapter{List of changes} |
|
\section*{Version 1.3 (2022-08-28)} |
|
This release removes support for temporarily blocked interrupts (interrupts can still be disabled) and introduces wake-up interrupts. |
|
\section*{Version 1.2 (2021-10-21)} |
|
This release introduces a few non-breaking changes to the software and testbench. The CPU RTL description hasn't been changed from the previous release. |
/rtl/lxp32_cpu.vhd
89,6 → 89,7
signal interrupt_vector: std_logic_vector(2 downto 0); |
signal interrupt_ready: std_logic; |
signal interrupt_return: std_logic; |
signal interrupt_wakeup: std_logic; |
|
begin |
|
131,6 → 132,8
interrupt_valid_i=>interrupt_valid, |
interrupt_vector_i=>interrupt_vector, |
interrupt_ready_o=>interrupt_ready, |
|
wakeup_i=>interrupt_wakeup, |
|
sp_raddr1_o=>sp_raddr1, |
sp_rdata1_i=>sp_rdata1, |
247,6 → 250,8
interrupt_vector_o=>interrupt_vector, |
interrupt_ready_i=>interrupt_ready, |
interrupt_return_i=>interrupt_return, |
|
wakeup_o=>interrupt_wakeup, |
|
sp_waddr_i=>sp_waddr, |
sp_we_i=>sp_we, |
/rtl/lxp32_decode.vhd
27,6 → 27,8
interrupt_valid_i: in std_logic; |
interrupt_vector_i: in std_logic_vector(2 downto 0); |
interrupt_ready_o: out std_logic; |
|
wakeup_i: in std_logic; |
|
sp_raddr1_o: out std_logic_vector(7 downto 0); |
sp_rdata1_i: in std_logic_vector(31 downto 0); |
100,6 → 102,7
-- Signals related to interrupt handling |
|
signal interrupt_ready: std_logic:='0'; |
signal wakeup_reg: std_logic:='0'; |
|
begin |
|
148,8 → 151,10
op3_o<=(others=>'-'); |
jump_type_o<=(others=>'-'); |
dst_out<=(others=>'-'); |
wakeup_reg<='0'; |
else |
interrupt_ready<='0'; |
wakeup_reg<=wakeup_reg or wakeup_i; |
if jump_valid_i='1' then |
valid_out<='0'; |
self_busy<='0'; |
257,6 → 262,7
elsif opcode="000010" then |
valid_out<='0'; |
self_busy<='1'; |
wakeup_reg<='0'; |
state<=Halt; |
elsif opcode(5 downto 4)="11" then |
valid_out<='1'; |
285,7 → 291,7
when ContinueInterrupt => |
valid_out<='0'; |
when Halt => |
if interrupt_valid_i='1' then |
if interrupt_valid_i='1' or wakeup_i='1' or wakeup_reg='1' then |
self_busy<='0'; |
state<=Regular; |
end if; |
/rtl/lxp32_interrupt_mux.vhd
24,6 → 24,8
interrupt_vector_o: out std_logic_vector(2 downto 0); |
interrupt_ready_i: in std_logic; |
interrupt_return_i: in std_logic; |
|
wakeup_o: out std_logic; |
|
sp_waddr_i: in std_logic_vector(7 downto 0); |
sp_we_i: in std_logic; |
43,15 → 45,13
signal interrupt_valid: std_logic:='0'; |
|
signal interrupts_enabled: std_logic_vector(7 downto 0):=(others=>'0'); |
signal interrupts_blocked: std_logic_vector(7 downto 0):=(others=>'0'); |
signal interrupts_wakeup: std_logic_vector(7 downto 0):=(others=>'0'); |
|
begin |
|
-- Note: "disabled" interrupts (i.e. for which interrupts_enabled_i(i)='0') |
-- are ignored completely, meaning that the interrupt handler won't be |
-- called even if the interrupt is enabled later. Conversely, "blocked" |
-- interrupts are registered, but their handlers are not called until they |
-- are unblocked. |
-- called even if the interrupt is enabled later. |
|
process (clk_i) is |
begin |
62,17 → 62,18
state<=Ready; |
interrupt_valid<='0'; |
interrupt_vector_o<=(others=>'-'); |
wakeup_o<='0'; |
else |
irq_reg<=irq_i; |
|
pending_interrupts<=(pending_interrupts or |
(irq_i and not irq_reg)) and |
interrupts_enabled; |
interrupts_enabled and not interrupts_wakeup; |
|
case state is |
when Ready => |
for i in pending_interrupts'reverse_range loop -- lower interrupts have priority |
if pending_interrupts(i)='1' and interrupts_blocked(i)='0' then |
if pending_interrupts(i)='1' then |
pending_interrupts(i)<='0'; |
interrupt_valid<='1'; |
interrupt_vector_o<=std_logic_vector(to_unsigned(i,3)); |
90,6 → 91,12
state<=Ready; |
end if; |
end case; |
|
if (irq_i and (not irq_reg) and interrupts_enabled and interrupts_wakeup)/=X"00" then |
wakeup_o<='1'; |
else |
wakeup_o<='0'; |
end if; |
end if; |
end if; |
end process; |
101,10 → 108,10
if rising_edge(clk_i) then |
if rst_i='1' then |
interrupts_enabled<=(others=>'0'); |
interrupts_blocked<=(others=>'0'); |
interrupts_wakeup<=(others=>'0'); |
elsif sp_we_i='1' and sp_waddr_i=X"FC" then |
interrupts_enabled<=sp_wdata_i(7 downto 0); |
interrupts_blocked<=sp_wdata_i(15 downto 8); |
interrupts_wakeup<=sp_wdata_i(15 downto 8); |
end if; |
end if; |
end process; |
/tools/bin/lxp32asm.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/tools/bin/lxp32dump.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/tools/bin/wigen.exe
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/verify/lxp32/src/firmware/test016.asm
1,47 → 1,28
/* |
* Test for temporarily blocked interrupts |
* Test wake-up interrupts |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) |
lc r104, 0x20000004 // timer: delay between pulses (in cycles) |
lc r102, 0x30000000 // coprocessor input register |
lc r103, 0x30000004 // coprocessor output register |
lc r104, failure |
|
lcs cr, 0x0404 // enable coprocessor interrupt and mark it as wake-up |
|
lc iv0, timer_handler |
lc cr, 0x101 // enable interrupt 0 in temporarily blocked state |
sw r102, 33 |
|
lc r32, 0 // interrupt handler call counter |
lc r33, 1000 // loop counter |
lc r34, loop1 |
lc r35, loop2 |
|
sw r104, 100 |
sw r103, 1 |
hlt |
|
loop1: |
sub r33, r33, 1 |
cjmpug r34, r33, 0 // loop1 |
|
lc r33, 1000 |
mov cr, 1 // unblock interrupt 0 |
|
loop2: |
sub r33, r33, 1 |
cjmpug r35, r33, 0 // loop2 |
|
// r32 should be 1 by this point |
cjmpne r102, r32, 1 // failure |
sw r100, 1 |
lw r1, r103 |
cjmpne r104, r1, 99 |
|
sw r100, 1 // success |
|
halt: |
hlt |
jmp r101 // halt |
|
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
timer_handler: |
add r32, r32, 1 |
iret |
/verify/lxp32/src/platform/coprocessor.vhd
38,7 → 38,7
|
signal value: unsigned(31 downto 0):=(others=>'0'); |
signal result: unsigned(31 downto 0):=(others=>'0'); |
signal cnt: integer range 0 to 5:=0; |
signal cnt: integer range 0 to 50:=0; |
signal irq: std_logic:='0'; |
|
begin |
67,7 → 67,7
if wbs_adr_i="00"&X"000000" then |
value(i*8+7 downto i*8)<= |
unsigned(wbs_dat_i(i*8+7 downto i*8)); |
cnt<=5; |
cnt<=50; |
end if; |
end if; |
end loop; |