1 |
2 |
jclaytons |
//---------------------------------------------------------------------------
|
2 |
|
|
// RISC 16F84 "clk2x" core
|
3 |
|
|
//
|
4 |
|
|
// This file is part of the "risc_16F84" project.
|
5 |
|
|
// http://www.opencores.org/cores/risc_16F84
|
6 |
|
|
//
|
7 |
|
|
//
|
8 |
|
|
// Description: See description below (which suffices for IP core
|
9 |
|
|
// specification document.)
|
10 |
|
|
//
|
11 |
|
|
// Copyright (C) 1999 Sumio Morioka (original VHDL design version)
|
12 |
|
|
// Copyright (C) 2001 John Clayton and OPENCORES.ORG (this Verilog version)
|
13 |
|
|
//
|
14 |
|
|
// NOTE: This source code is free for educational/hobby use only. It cannot
|
15 |
|
|
// be used for commercial purposes without the consent of Microchip
|
16 |
|
|
// Technology incorporated.
|
17 |
|
|
//
|
18 |
|
|
// This source file may be used and distributed without restriction provided
|
19 |
|
|
// that this copyright statement is not removed from the file and that any
|
20 |
|
|
// derivative work contains the original copyright notice and the associated
|
21 |
|
|
// disclaimer.
|
22 |
|
|
//
|
23 |
|
|
// This source file is free software; you can redistribute it and/or modify
|
24 |
|
|
// it under the terms of the GNU Lesser General Public License as published
|
25 |
|
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
26 |
|
|
// (at your option) any later version.
|
27 |
|
|
//
|
28 |
|
|
// This source is distributed in the hope that it will be useful, but WITHOUT
|
29 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
30 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
31 |
|
|
// License for more details.
|
32 |
|
|
//
|
33 |
|
|
// You should have received a copy of the GNU Lesser General Public License
|
34 |
|
|
// along with this source.
|
35 |
|
|
// If not, download it from http://www.opencores.org/lgpl.shtml
|
36 |
|
|
//
|
37 |
|
|
//---------------------------------------------------------------------------
|
38 |
|
|
//
|
39 |
|
|
// Author: John Clayton
|
40 |
|
|
// Date : January 29, 2002
|
41 |
|
|
//
|
42 |
|
|
// (NOTE: Date formatted as day/month/year.)
|
43 |
|
|
// Update: 29/01/02 copied this file from memory_sizer.v (pared down).
|
44 |
|
|
// Translated the module and signal declarations.
|
45 |
|
|
// Transformed the instruction wires to lowercase.
|
46 |
|
|
// Transformed the addressing wires to lowercase.
|
47 |
|
|
// Update: 31/01/02 Translated the instruction decoder.
|
48 |
|
|
// Update: 5/02/02 Determined that stack is simply a circular buffer of
|
49 |
|
|
// 8 locations, 13 bits per location. Started translating
|
50 |
|
|
// "main_efsm" process. Added all code from piccore.vhd
|
51 |
|
|
// into this file for eventual translation. Concluded that
|
52 |
|
|
// "stack_full_node" is not needed.
|
53 |
|
|
// Update: 6/02/02 Translated the "ram_i_node" if/else precedural assignment.
|
54 |
|
|
// Update: 7/02/02 Changed all := to <=, changed all '0' to 0 and '1' to 1.
|
55 |
|
|
// Replaced all " downto " with ":".
|
56 |
|
|
// Finished translating QRESET state.
|
57 |
|
|
// Update: 20/02/02 Replaced all instances of Qreset with QRESET_PP. Also
|
58 |
|
|
// replaced other state designations with their new names.
|
59 |
|
|
// Finished translating Q1, Q2 states.
|
60 |
|
|
// Update: 22/02/02 Translated section 2-4-1-1 (aluout register)
|
61 |
|
|
// Update: 27/02/02 Replaced all "or" with "||" in if statements
|
62 |
|
|
// Replaced all "and" with "&&" in if statements.
|
63 |
|
|
// Replaced all "not" with "~" in if statements.
|
64 |
|
|
// Finished translating Q3,Q4 states.
|
65 |
|
|
// Translated output signal assignments at end of code.
|
66 |
|
|
// Translated interrupt trigger processes.
|
67 |
|
|
// Update: 28/02/02 Finished translation of WDT and TMR0 prescaler.
|
68 |
|
|
// Trimmed line length to 80 characters throughout.
|
69 |
|
|
// Prepared to attempt initial syntax checking.
|
70 |
|
|
// Cleaned up some naming conventions, and verified that
|
71 |
|
|
// all I/O pins have _i or _o appended in the body of the
|
72 |
|
|
// code.
|
73 |
|
|
// Update: 03/04/02 Changed "progdata_i" to "prog_dat_i" Also changed
|
74 |
|
|
// "progadr_o" to "prog_adr_o"
|
75 |
|
|
// Update: 04/04/02 Created new file "risc16f84_lite.v" This file is reduced
|
76 |
|
|
// and simplified from the original "risc16f84.v" file.
|
77 |
|
|
// Specifically, I am removing EEPROM support, and
|
78 |
|
|
// consolidating porta and portb I/O pins so that they
|
79 |
|
|
// are bidirectional.
|
80 |
|
|
// Update: 04/04/02 Created a new file "risc16f84_small.v" This file is
|
81 |
|
|
// further reduced and simplified from "risc16f84_lite.v"
|
82 |
|
|
// Specifically, I am removing the prescaler, TMR0 and WDT.
|
83 |
|
|
// Also, I am removing support for portb interrupts, leaving
|
84 |
|
|
// only rb0/int as an interrupt source. This pin will be
|
85 |
|
|
// the only way to wake up from the SLEEP instruction...
|
86 |
|
|
// Obviously, the CLEARWDT instruction will no longer do
|
87 |
|
|
// anything.
|
88 |
|
|
// Update: 05/04/02 Removed the "powerdown_o", "startclk_o" and "clk_o" pins
|
89 |
|
|
// from the small design. Also removed "rbpu_o", so if you
|
90 |
|
|
// want pullups, you have to add them explicitly in the
|
91 |
|
|
// constraints file, and option_reg[7] doesn't control them.
|
92 |
|
|
// Update: 08/04/02 Decided to modify "risc16f84_small.v" in order to try for
|
93 |
|
|
// more performance (only 2 states per instruction!)
|
94 |
|
|
// The new file is called "risc16f84_clk2x.v" The resulting
|
95 |
|
|
// code was synthesized, but not tested yet.
|
96 |
|
|
// Update: 11/04/02 Decided to remove porta and portb from this unit, and add
|
97 |
|
|
// instead an auxiliary bus, which is intended to allow I/O
|
98 |
|
|
// using an indirect approach, similar to using the FSR.
|
99 |
|
|
// However, the aux_adr_o is 16 bits wide, so that larger
|
100 |
|
|
// RAM may be accessed indirectly by the processor... The use
|
101 |
|
|
// of FSR for this purpose proved undesirable, since any new
|
102 |
|
|
// page of RAM contains "holes" to accomodate the registers
|
103 |
|
|
// in the first 12 locations (including FSR!) so that large
|
104 |
|
|
// contiguous blocks of memory could not be accessed in an
|
105 |
|
|
// effective way. This auxiliary bus solves that problem.
|
106 |
|
|
// Since this processor is implemented inside of an FPGA,
|
107 |
|
|
// and it is not a goal to maintain compatibility with
|
108 |
|
|
// existing libraries of code, there is no need to maintain
|
109 |
|
|
// porta and portb in the hardware.
|
110 |
|
|
// The aux_adr_lo and aux_adr_hi registers are located at
|
111 |
|
|
// 88h and 89h, and the aux_dat_io location is decoded at
|
112 |
|
|
// 08h.
|
113 |
|
|
// Also, changed to using "ram_we_o" instead of "readram_o"
|
114 |
|
|
// and "writeram_o"
|
115 |
|
|
// Update: 16/04/02 Added clock enable signal, for processor single stepping.
|
116 |
|
|
// "aux_dat_io" is only driven when "clk_en_i" is high...
|
117 |
|
|
// Update: 17/04/02 Removed "reset_condition" and moved "inc_pc_node" out of
|
118 |
|
|
// the clocking area, making it non-registered. In fact, I
|
119 |
|
|
// moved everything other than the state machine out of the
|
120 |
|
|
// clocked logic section. Changed "aluout_reg" to "aluout"
|
121 |
|
|
// since it is no longer registered.
|
122 |
|
|
// Update: 26/04/02 Fixed bug in aluout logic. The AND and OR functions were
|
123 |
|
|
// coded with logical AND/OR instead of bitwise AND/OR!
|
124 |
|
|
// Update: 26/04/02 Changed location of aux_adr_lo and aux_adr_hi registers
|
125 |
|
|
// to 05h and 06h, respectively. This was done to save
|
126 |
|
|
// code space because when using the aux data bus, no bank
|
127 |
|
|
// switching is necessary since they will now reside in the
|
128 |
|
|
// same bank.
|
129 |
|
|
// Update: 01/05/02 Fixed another bug -- the rrf and rlf instructions were
|
130 |
|
|
// coded incorrectly.
|
131 |
|
|
// Update: 03/05/02 Fixed another bug -- the carry bit was incorrect (the
|
132 |
|
|
// problem was discovered while performing SUBWF X,W where
|
133 |
|
|
// W contained 0 and X contained 1. (1-0). The logic for
|
134 |
|
|
// the carry bit appears to have been incorrect even in
|
135 |
|
|
// the original VHDL code by Sumio Morioka.
|
136 |
|
|
// Update: 11/18/02 Fixed bug in PCL addressing mode (near line 791)
|
137 |
|
|
// Removed parameters associated with WDT.
|
138 |
|
|
// Update: 11/25/02 Re-wrote much of the main FSM. Attempted to generate logic
|
139 |
|
|
// to recognize the falling edge of an interrupt, and was
|
140 |
|
|
// unsuccessful (not simulation, actual hardware tests.)
|
141 |
|
|
// Realized that falling edge interrupt can be equivalent to
|
142 |
|
|
// rising edge interrupt with a NOT gate on the signal. Since
|
143 |
|
|
// NOTs are practically free inside of an FPGA, decided to
|
144 |
|
|
// abandon the negative edge aspect of recognizing interrupts.
|
145 |
|
|
// Therefore, removed the "option_reg" since it is no longer
|
146 |
|
|
// needed in this design.
|
147 |
|
|
// Update: 08/08/03 Fixed a mathematical error, which was introduced by the bug
|
148 |
|
|
// fix of 03/05/02. The fix of 03/05/02 correctly generated the
|
149 |
|
|
// C bit for subtraction of zero, but unfortunately it introduced
|
150 |
|
|
// an error such that all subtraction results were off by 1.
|
151 |
|
|
// Obviously, this was unacceptable, and I think it has been fixed
|
152 |
|
|
// by the new signals "c_subtract_zero" and "c_dig_subtract_zero"
|
153 |
4 |
jclaytons |
// Update: 10/24/05 Added code patches to fix interrupt bug and status flag updates
|
154 |
|
|
// when using literal value of 0x03. These bugs were reported by
|
155 |
|
|
// an opencores.org user. Added three "disable_status_x" signals.
|
156 |
6 |
jclaytons |
// Modified file still needs to be tested.
|
157 |
10 |
jclaytons |
// Update: 06/29/13 This project is now CC-BY licensed. See risc16f84_license.txt
|
158 |
|
|
// for details.
|
159 |
2 |
jclaytons |
//
|
160 |
|
|
// Description
|
161 |
|
|
//---------------------------------------------------------------------------
|
162 |
|
|
// This logic module implements a small RISC microcontroller, with functions
|
163 |
|
|
// and instruction set very similar to those of the Microchip 16F84 chip.
|
164 |
|
|
// This work is a translation (from VHDL to Verilog) of the "CQPIC" design
|
165 |
|
|
// published in 1999 by Sumio Morioka of Japan, and published in the December
|
166 |
|
|
// 1999 issue of "Transistor Gijutsu Magazine." The translation was performed
|
167 |
|
|
// by John Clayton, without the use of any translation tools.
|
168 |
|
|
//
|
169 |
|
|
// Original version used as basis for translation: CQPIC version 1.00b
|
170 |
|
|
// (December 10, 2000)
|
171 |
|
|
//
|
172 |
|
|
// Further revisions and re-writing have been completed on this code by John
|
173 |
|
|
// Clayton. The interrupt mechanism has been completely re-done, and the
|
174 |
|
|
// way in which the program counter is generated is expressed in a new way.
|
175 |
|
|
//
|
176 |
|
|
// In the comments, a "cycle" is defined as a processor cycle of 2 states.
|
177 |
|
|
// Thus, passing through states Q2_PP and Q4_PP completes one cycle.
|
178 |
|
|
// The numbers "1-3" and so forth are left from the comments in the original
|
179 |
|
|
// source code used as the basis of the translation.
|
180 |
|
|
//---------------------------------------------------------------------------
|
181 |
|
|
|
182 |
|
|
`define STATEBIT_SIZE 2 // Size of state machine register (bits)
|
183 |
|
|
|
184 |
|
|
|
185 |
|
|
module risc16f84_clk2x (
|
186 |
|
|
prog_dat_i, // [13:0] ROM read data
|
187 |
|
|
prog_adr_o, // [12:0] ROM address
|
188 |
|
|
ram_dat_i, // [7:0] RAM read data
|
189 |
|
|
ram_dat_o, // [7:0] RAM write data
|
190 |
|
|
ram_adr_o, // [8:0] RAM address; ram_adr[8:7] indicates RAM-BANK
|
191 |
|
|
ram_we_o, // RAM write strobe (H active)
|
192 |
|
|
aux_adr_o, // [15:0] Auxiliary address bus
|
193 |
|
|
aux_dat_io, // [7:0] Auxiliary data bus (tri-state bidirectional)
|
194 |
|
|
aux_we_o, // Auxiliary write strobe (H active)
|
195 |
|
|
int0_i, // PORT-B(0) INT
|
196 |
|
|
reset_i, // Power-on reset (H active)
|
197 |
|
|
clk_en_i, // Clock enable for all clocked logic
|
198 |
|
|
clk_i // Clock input
|
199 |
|
|
);
|
200 |
|
|
|
201 |
|
|
|
202 |
|
|
// You can change the following parameters as you would like
|
203 |
|
|
parameter STACK_SIZE_PP = 8; // Size of PC stack
|
204 |
|
|
parameter LOG2_STACK_SIZE_PP = 3; // Log_2(stack_size)
|
205 |
|
|
|
206 |
|
|
// State definitions for state machine, provided as parameters to allow
|
207 |
|
|
// for redefinition of state values by the instantiator if desired.
|
208 |
|
|
parameter Q2_PP = 2'b00; // state Q2
|
209 |
|
|
parameter Q4_PP = 2'b01; // state Q4
|
210 |
|
|
parameter QINT_PP = 2'b10; // interrupt state (substitute for Q4)
|
211 |
|
|
parameter QSLEEP_PP = 2'b11; // sleep state
|
212 |
|
|
|
213 |
|
|
|
214 |
|
|
// I/O declarations
|
215 |
|
|
|
216 |
|
|
// program ROM data bus/address bus
|
217 |
|
|
input [13:0] prog_dat_i; // ROM read data
|
218 |
|
|
output [12:0] prog_adr_o; // ROM address
|
219 |
|
|
|
220 |
|
|
// data RAM data bus/address bus/control signals
|
221 |
|
|
input [7:0] ram_dat_i; // RAM read data
|
222 |
|
|
output [7:0] ram_dat_o; // RAM write data
|
223 |
|
|
output [8:0] ram_adr_o; // RAM address; ram_adr[8:7] indicates RAM-BANK
|
224 |
|
|
output ram_we_o; // RAM write strobe (H active)
|
225 |
|
|
|
226 |
|
|
// auxiliary data bus/address bus/control signals
|
227 |
|
|
output [15:0] aux_adr_o; // AUX address bus
|
228 |
|
|
inout [7:0] aux_dat_io; // AUX data bus
|
229 |
|
|
output aux_we_o; // AUX write strobe (H active)
|
230 |
|
|
|
231 |
|
|
// interrupt input
|
232 |
|
|
input int0_i; // INT
|
233 |
|
|
|
234 |
|
|
// CPU reset
|
235 |
|
|
input reset_i; // Power-on reset (H active)
|
236 |
|
|
|
237 |
|
|
// CPU clock
|
238 |
|
|
input clk_en_i; // Clock enable input
|
239 |
|
|
input clk_i; // Clock input
|
240 |
|
|
|
241 |
|
|
|
242 |
|
|
// Internal signal declarations
|
243 |
|
|
|
244 |
|
|
// User registers
|
245 |
|
|
reg [7:0] w_reg; // W
|
246 |
|
|
reg [12:0] pc_reg; // PCH/PCL -- Address currently being fetched
|
247 |
|
|
reg [12:0] old_pc_reg; // Address fetched previous to this one.
|
248 |
|
|
reg [7:0] status_reg; // STATUS
|
249 |
|
|
reg [7:0] fsr_reg; // FSR
|
250 |
|
|
reg [4:0] pclath_reg; // PCLATH
|
251 |
|
|
reg [7:0] intcon_reg; // INTCON
|
252 |
|
|
reg [7:0] aux_adr_hi_reg; // AUX address high byte
|
253 |
|
|
reg [7:0] aux_adr_lo_reg; // AUX address low byte
|
254 |
|
|
|
255 |
|
|
// Internal registers for controlling instruction execution
|
256 |
|
|
reg [13:0] inst_reg; // Holds fetched op-code/operand
|
257 |
|
|
reg [7:0] aluinp1_reg; // data source (1 of 2)
|
258 |
|
|
reg [7:0] aluinp2_reg; // data source (2 of 2)
|
259 |
|
|
reg exec_stall_reg; // if H (i.e. after GOTO etc), stall execution.
|
260 |
|
|
|
261 |
|
|
// Stack
|
262 |
|
|
// stack (array of data-registers)
|
263 |
|
|
reg [12:0] stack_reg [STACK_SIZE_PP-1:0];
|
264 |
|
|
// stack pointer
|
265 |
|
|
reg [LOG2_STACK_SIZE_PP-1:0] stack_pnt_reg;
|
266 |
|
|
wire [12:0] stack_top; // More compatible with sensitivity list than
|
267 |
|
|
// "stack_reg[stack_pnt_reg]"
|
268 |
|
|
|
269 |
|
|
// Interrupt registers/nodes
|
270 |
|
|
wire int_condition; // Indicates that an interrupt should be recognized
|
271 |
|
|
wire intrise; // High indicates edge was detected
|
272 |
|
|
reg intrise_reg; // detect positive edge of PORT-B inputs
|
273 |
|
|
// Synchronizer for interrupt
|
274 |
|
|
reg inte_sync_reg;
|
275 |
|
|
|
276 |
|
|
// State register
|
277 |
|
|
reg [`STATEBIT_SIZE-1:0] state_reg;
|
278 |
|
|
reg [`STATEBIT_SIZE-1:0] next_state_node;
|
279 |
|
|
|
280 |
|
|
// Result of decoding instruction -- only 1 is active at a time
|
281 |
|
|
wire inst_addlw;
|
282 |
|
|
wire inst_addwf;
|
283 |
|
|
wire inst_andlw;
|
284 |
|
|
wire inst_andwf;
|
285 |
|
|
wire inst_bcf;
|
286 |
|
|
wire inst_bsf;
|
287 |
|
|
wire inst_btfsc;
|
288 |
|
|
wire inst_btfss;
|
289 |
|
|
wire inst_call;
|
290 |
|
|
wire inst_clrf;
|
291 |
|
|
wire inst_clrw;
|
292 |
|
|
wire inst_comf;
|
293 |
|
|
wire inst_decf;
|
294 |
|
|
wire inst_decfsz;
|
295 |
|
|
wire inst_goto;
|
296 |
|
|
wire inst_incf;
|
297 |
|
|
wire inst_incfsz;
|
298 |
|
|
wire inst_iorlw;
|
299 |
|
|
wire inst_iorwf;
|
300 |
|
|
wire inst_movlw;
|
301 |
|
|
wire inst_movf;
|
302 |
|
|
wire inst_movwf;
|
303 |
|
|
wire inst_retfie;
|
304 |
|
|
wire inst_retlw;
|
305 |
|
|
wire inst_ret;
|
306 |
|
|
wire inst_rlf;
|
307 |
|
|
wire inst_rrf;
|
308 |
|
|
wire inst_sleep;
|
309 |
|
|
wire inst_sublw;
|
310 |
|
|
wire inst_subwf;
|
311 |
|
|
wire inst_swapf;
|
312 |
|
|
wire inst_xorlw;
|
313 |
|
|
wire inst_xorwf;
|
314 |
|
|
|
315 |
|
|
// Result of calculating RAM access address
|
316 |
|
|
wire [8:0] ram_adr_node; // RAM access address
|
317 |
|
|
|
318 |
|
|
// These wires indicate accesses to special registers...
|
319 |
|
|
// Only 1 is active at a time.
|
320 |
|
|
wire addr_pcl;
|
321 |
|
|
wire addr_stat;
|
322 |
|
|
wire addr_fsr;
|
323 |
|
|
wire addr_pclath;
|
324 |
|
|
wire addr_intcon;
|
325 |
|
|
wire addr_aux_adr_lo;
|
326 |
|
|
wire addr_aux_adr_hi;
|
327 |
|
|
wire addr_aux_dat;
|
328 |
|
|
wire addr_sram;
|
329 |
|
|
|
330 |
|
|
// Other output registers (for removing hazards)
|
331 |
|
|
reg ram_we_reg; // data-sram write strobe
|
332 |
|
|
reg aux_we_reg; // AUX write strobe
|
333 |
|
|
|
334 |
|
|
|
335 |
|
|
// Signals used in "main_efsm" procedure
|
336 |
|
|
// (Intermediate nodes used for resource sharing.)
|
337 |
|
|
wire [7:0] ram_i_node; // result of reading RAM/Special registers
|
338 |
|
|
wire [7:0] mask_node; // bit mask for logical operations
|
339 |
|
|
wire [8:0] add_node; // result of 8bit addition
|
340 |
|
|
wire [4:0] addlow_node; // result of low-4bit addition
|
341 |
|
|
wire aluout_zero_node; // H if ALUOUT = 0
|
342 |
|
|
|
343 |
|
|
reg [12:0] next_pc_node; // value of next PC
|
344 |
|
|
reg [7:0] aluout; // result of calculation
|
345 |
|
|
reg writew_node; // H if destination is W register
|
346 |
|
|
reg writeram_node; // H if destination is RAM/Special registers
|
347 |
|
|
reg c_subtract_zero; // High for special case of C bit, when subtracting zero
|
348 |
|
|
reg c_dig_subtract_zero; // High for special case of C bit, when subtracting zero
|
349 |
|
|
|
350 |
|
|
wire next_exec_stall;
|
351 |
4 |
jclaytons |
// Three signals used to disable status flag updates. Fixes bug when literal 0x03 is used.
|
352 |
|
|
wire disable_status_z;
|
353 |
|
|
wire disable_status_c;
|
354 |
|
|
wire disable_status_dc;
|
355 |
2 |
jclaytons |
|
356 |
|
|
//--------------------------------------------------------------------------
|
357 |
|
|
// Instantiations
|
358 |
|
|
//--------------------------------------------------------------------------
|
359 |
|
|
|
360 |
|
|
|
361 |
|
|
//--------------------------------------------------------------------------
|
362 |
|
|
// Functions & Tasks
|
363 |
|
|
//--------------------------------------------------------------------------
|
364 |
|
|
|
365 |
|
|
//--------------------------------------------------------------------------
|
366 |
|
|
// Module code
|
367 |
|
|
//--------------------------------------------------------------------------
|
368 |
|
|
|
369 |
|
|
// This represents the instruction fetch from program memory.
|
370 |
|
|
// inst_reg[13:0] stores the instruction. This happens at the end of Q4.
|
371 |
|
|
// So the memory access time is one processor cycle (2 clocks!) minus
|
372 |
|
|
// the setup-time of this register, and minus the delay to drive the
|
373 |
|
|
// address out onto the prog_adr_o bus.
|
374 |
|
|
always @(posedge clk_i)
|
375 |
|
|
begin
|
376 |
|
|
if (reset_i) inst_reg <= 0;
|
377 |
|
|
else if (clk_en_i && (state_reg == Q4_PP)) inst_reg <= prog_dat_i;
|
378 |
|
|
end
|
379 |
|
|
|
380 |
|
|
// NOTE: There is an extra "15th" bit of inst_reg, which represents an
|
381 |
|
|
// interrupt execution cycle. This is included in inst_reg so that when
|
382 |
|
|
// an interrupt instruction is executing, it effectively "pre-empts" the
|
383 |
|
|
// other instructions.
|
384 |
|
|
// The fifteenth bit, inst_reg[14], is set by the interrupt logic.
|
385 |
|
|
|
386 |
|
|
// Decode OPcode (see pp.54 of PIC16F84 data sheet)
|
387 |
|
|
// only 1 signal of the following signals will be '1'
|
388 |
|
|
assign inst_call = (inst_reg[13:11] == 3'b100 );
|
389 |
|
|
assign inst_goto = (inst_reg[13:11] == 3'b101 );
|
390 |
|
|
assign inst_bcf = (inst_reg[13:10] == 4'b0100 );
|
391 |
|
|
assign inst_bsf = (inst_reg[13:10] == 4'b0101 );
|
392 |
|
|
assign inst_btfsc = (inst_reg[13:10] == 4'b0110 );
|
393 |
|
|
assign inst_btfss = (inst_reg[13:10] == 4'b0111 );
|
394 |
|
|
assign inst_movlw = (inst_reg[13:10] == 4'b1100 );
|
395 |
|
|
assign inst_retlw = (inst_reg[13:10] == 4'b1101 );
|
396 |
|
|
assign inst_sublw = (inst_reg[13:9] == 5'b11110 );
|
397 |
|
|
assign inst_addlw = (inst_reg[13:9] == 5'b11111 );
|
398 |
|
|
assign inst_iorlw = (inst_reg[13:8] == 6'b111000 );
|
399 |
|
|
assign inst_andlw = (inst_reg[13:8] == 6'b111001 );
|
400 |
|
|
assign inst_xorlw = (inst_reg[13:8] == 6'b111010 );
|
401 |
|
|
assign inst_subwf = (inst_reg[13:8] == 6'b000010 );
|
402 |
|
|
assign inst_decf = (inst_reg[13:8] == 6'b000011 );
|
403 |
|
|
assign inst_iorwf = (inst_reg[13:8] == 6'b000100 );
|
404 |
|
|
assign inst_andwf = (inst_reg[13:8] == 6'b000101 );
|
405 |
|
|
assign inst_xorwf = (inst_reg[13:8] == 6'b000110 );
|
406 |
|
|
assign inst_addwf = (inst_reg[13:8] == 6'b000111 );
|
407 |
|
|
assign inst_movf = (inst_reg[13:8] == 6'b001000 );
|
408 |
|
|
assign inst_comf = (inst_reg[13:8] == 6'b001001 );
|
409 |
|
|
assign inst_incf = (inst_reg[13:8] == 6'b001010 );
|
410 |
|
|
assign inst_decfsz = (inst_reg[13:8] == 6'b001011 );
|
411 |
|
|
assign inst_rrf = (inst_reg[13:8] == 6'b001100 );
|
412 |
|
|
assign inst_rlf = (inst_reg[13:8] == 6'b001101 );
|
413 |
|
|
assign inst_swapf = (inst_reg[13:8] == 6'b001110 );
|
414 |
|
|
assign inst_incfsz = (inst_reg[13:8] == 6'b001111 );
|
415 |
|
|
assign inst_movwf = (inst_reg[13:7] == 7'b0000001 );
|
416 |
|
|
assign inst_clrw = (inst_reg[13:7] == 7'b0000010 );
|
417 |
|
|
assign inst_clrf = (inst_reg[13:7] == 7'b0000011 );
|
418 |
|
|
assign inst_ret = (inst_reg[13:0] == 14'b00000000001000);
|
419 |
|
|
assign inst_retfie = (inst_reg[13:0] == 14'b00000000001001);
|
420 |
|
|
assign inst_sleep = (inst_reg[13:0] == 14'b00000001100011);
|
421 |
|
|
|
422 |
|
|
|
423 |
|
|
// Calculate RAM access address (see pp.19 of PIC16F84 data sheet)
|
424 |
|
|
|
425 |
|
|
// if "d"=0, indirect addressing is used, so RAM address is BANK+FSR
|
426 |
|
|
// otherwise, RAM address is BANK+"d"
|
427 |
|
|
// (see pp.19 of PIC16F84 data sheet)
|
428 |
|
|
assign ram_adr_node = (inst_reg[6:0]==0)?{status_reg[7],fsr_reg[7:0]}:
|
429 |
|
|
{status_reg[6:5],inst_reg[6:0]};
|
430 |
|
|
|
431 |
|
|
// check if this is an access to external RAM or not
|
432 |
|
|
assign addr_sram = (ram_adr_node[6:0] > 7'b0001011); //0CH-7FH,8CH-FFH
|
433 |
|
|
|
434 |
|
|
// check if this is an access to special register or not
|
435 |
|
|
// only 1 signal of the following signals will be '1'
|
436 |
|
|
assign addr_pcl = (ram_adr_node[6:0] == 7'b0000010); // 02H, 82H
|
437 |
|
|
assign addr_stat = (ram_adr_node[6:0] == 7'b0000011); // 03H, 83H
|
438 |
|
|
assign addr_fsr = (ram_adr_node[6:0] == 7'b0000100); // 04H, 84H
|
439 |
|
|
assign addr_aux_dat = (ram_adr_node[7:0] == 8'b00001000); // 08H
|
440 |
|
|
assign addr_pclath = (ram_adr_node[6:0] == 7'b0001010); // 0AH, 8AH
|
441 |
|
|
assign addr_intcon = (ram_adr_node[6:0] == 7'b0001011); // 0BH, 8BH
|
442 |
|
|
assign addr_aux_adr_lo = (ram_adr_node[7:0] == 8'b00000101); // 05H
|
443 |
|
|
assign addr_aux_adr_hi = (ram_adr_node[7:0] == 8'b00000110); // 06H
|
444 |
|
|
|
445 |
|
|
// construct bit-mask for logical operations and bit tests
|
446 |
|
|
assign mask_node = 1 << inst_reg[9:7];
|
447 |
|
|
|
448 |
4 |
jclaytons |
// disable write access for status flags
|
449 |
|
|
assign disable_status_z = (inst_addwf || inst_andwf || inst_clrf ||
|
450 |
|
|
inst_clrw || inst_comf || inst_decf ||
|
451 |
|
|
inst_incf || inst_iorwf || inst_movf ||
|
452 |
|
|
inst_subwf || inst_xorwf || inst_addlw ||
|
453 |
|
|
inst_andlw || inst_iorlw || inst_iorlw ||
|
454 |
|
|
inst_sublw || inst_xorlw) ? 1:0;
|
455 |
|
|
assign disable_status_c = (inst_addwf || inst_subwf || inst_rlf ||
|
456 |
|
|
inst_rrf || inst_addlw || inst_sublw ) ? 1:0;
|
457 |
|
|
assign disable_status_dc = (inst_addwf || inst_subwf || inst_addlw ||
|
458 |
|
|
inst_sublw ) ? 1:0;
|
459 |
|
|
|
460 |
|
|
|
461 |
2 |
jclaytons |
// Create the exec_stall signal, based on the contents of the currently
|
462 |
|
|
// executing instruction (inst_reg). next_exec_stall reflects the state
|
463 |
|
|
// to assign to exec_stall following the conclusion of the next Q4 state.
|
464 |
|
|
// All of these instructions cause an execution stall in the next cycle
|
465 |
|
|
// because they modify the program counter, and a new value is presented
|
466 |
|
|
// for fetching during the stall cycle, during which time no instruction
|
467 |
|
|
// should be executed.
|
468 |
|
|
//
|
469 |
|
|
// The conditional instructions are given along with their conditions for
|
470 |
|
|
// execution. If the conditions are not met, there is no stall and nothing
|
471 |
|
|
// to execute.
|
472 |
|
|
assign next_exec_stall = (
|
473 |
|
|
inst_goto
|
474 |
|
|
|| inst_call
|
475 |
|
|
|| inst_ret
|
476 |
|
|
|| inst_retlw
|
477 |
|
|
|| inst_retfie
|
478 |
|
|
|| (
|
479 |
|
|
(inst_btfsc || inst_decfsz || inst_incfsz)
|
480 |
|
|
&& aluout_zero_node
|
481 |
|
|
)
|
482 |
|
|
|| (inst_btfss && ~aluout_zero_node)
|
483 |
|
|
|| (addr_pcl && writeram_node)
|
484 |
|
|
);
|
485 |
|
|
always @(posedge clk_i)
|
486 |
|
|
begin
|
487 |
|
|
if (reset_i) exec_stall_reg <= 0;
|
488 |
|
|
else if (clk_en_i && (state_reg == QINT_PP)) exec_stall_reg <= 1;
|
489 |
|
|
else if (clk_en_i && (state_reg == Q4_PP))
|
490 |
|
|
exec_stall_reg <= (next_exec_stall && ~exec_stall_reg);
|
491 |
|
|
// exec stall should never be generated during a stall cycle, because
|
492 |
|
|
// a stall cycle doesn't execute anything...
|
493 |
|
|
end
|
494 |
|
|
|
495 |
|
|
assign stack_top = stack_reg[stack_pnt_reg];
|
496 |
|
|
// Formulate the next pc_reg value (the program counter.)
|
497 |
|
|
// During stall cycles, the pc is simply incremented...
|
498 |
|
|
always @(
|
499 |
|
|
pc_reg
|
500 |
|
|
or pclath_reg
|
501 |
|
|
or aluout
|
502 |
|
|
or stack_pnt_reg
|
503 |
|
|
or stack_top
|
504 |
|
|
or inst_ret
|
505 |
|
|
or inst_retlw
|
506 |
|
|
or inst_retfie
|
507 |
|
|
or inst_goto
|
508 |
|
|
or inst_call
|
509 |
|
|
or inst_reg
|
510 |
|
|
or writeram_node
|
511 |
|
|
or addr_pcl
|
512 |
|
|
or exec_stall_reg
|
513 |
|
|
)
|
514 |
|
|
begin
|
515 |
|
|
if (~exec_stall_reg &&(inst_ret || inst_retlw || inst_retfie))
|
516 |
|
|
next_pc_node <= stack_top;
|
517 |
|
|
else if (~exec_stall_reg &&(inst_goto || inst_call))
|
518 |
|
|
next_pc_node <= {pclath_reg[4:3],inst_reg[10:0]};
|
519 |
|
|
else if (~exec_stall_reg && (writeram_node && addr_pcl))
|
520 |
|
|
// PCL is data-destination, but update the entire PC.
|
521 |
|
|
next_pc_node <= {pclath_reg[4:0],aluout};
|
522 |
|
|
else
|
523 |
|
|
next_pc_node <= pc_reg + 1;
|
524 |
|
|
end
|
525 |
|
|
|
526 |
|
|
// Set the program counter
|
527 |
|
|
// If the sleep instruction is executing, then the PC is not allowed to be
|
528 |
|
|
// updated, since the processor will "freeze" and the instruction being fetched
|
529 |
|
|
// during the sleep instruction must be executed upon wakeup interrupt.
|
530 |
|
|
// Obviously, if the PC were to change at the end of the sleep instruction, then
|
531 |
|
|
// a different (incorrect) address would be fetched during the sleep time.
|
532 |
|
|
always @(posedge clk_i)
|
533 |
|
|
begin
|
534 |
|
|
if (reset_i) begin
|
535 |
|
|
pc_reg <= 0;
|
536 |
|
|
old_pc_reg <= 0;
|
537 |
|
|
end
|
538 |
|
|
else if (clk_en_i && (state_reg == QINT_PP))
|
539 |
|
|
begin
|
540 |
|
|
old_pc_reg <= pc_reg;
|
541 |
|
|
pc_reg <= 4;
|
542 |
|
|
end
|
543 |
|
|
else if (clk_en_i && ~inst_sleep && (state_reg == Q4_PP))
|
544 |
|
|
begin
|
545 |
|
|
old_pc_reg <= pc_reg;
|
546 |
|
|
pc_reg <= next_pc_node;
|
547 |
|
|
end
|
548 |
|
|
end
|
549 |
|
|
|
550 |
|
|
// 1. Intermediate nodes for resource sharing
|
551 |
|
|
|
552 |
|
|
// Tri-state drivers instead of a huge selector... It produces smaller
|
553 |
|
|
// results, and runs faster.
|
554 |
|
|
assign ram_i_node = (addr_sram) ?ram_dat_i:8'bZ;
|
555 |
|
|
assign ram_i_node = (addr_pcl) ?pc_reg[7:0]:8'bZ;
|
556 |
|
|
assign ram_i_node = (addr_stat) ?status_reg:8'bZ;
|
557 |
|
|
assign ram_i_node = (addr_fsr) ?fsr_reg:8'bZ;
|
558 |
|
|
assign ram_i_node = (addr_aux_dat) ?aux_dat_io:8'bZ;
|
559 |
|
|
assign ram_i_node = (addr_pclath) ?{3'b0,pclath_reg}:8'bZ;
|
560 |
|
|
assign ram_i_node = (addr_intcon) ?intcon_reg:8'bZ;
|
561 |
|
|
assign ram_i_node = (addr_aux_adr_lo) ?aux_adr_lo_reg:8'bZ;
|
562 |
|
|
assign ram_i_node = (addr_aux_adr_hi) ?aux_adr_hi_reg:8'bZ;
|
563 |
|
|
|
564 |
|
|
// 1-3. Adder (ALU)
|
565 |
|
|
// full 8bit-addition, with carry in/out.
|
566 |
|
|
// Note that "temp" and "dtemp" are intended to be thrown away.
|
567 |
|
|
// Also, addlow_node[3:0] are thrown away.
|
568 |
|
|
// Even though they are assigned, they should never be used.
|
569 |
|
|
assign add_node = {1'b0,aluinp1_reg}
|
570 |
|
|
+ {1'b0,aluinp2_reg};
|
571 |
|
|
// lower 4bit-addition
|
572 |
|
|
assign addlow_node = {1'b0,aluinp1_reg[3:0]}
|
573 |
|
|
+ {1'b0,aluinp2_reg[3:0]};
|
574 |
|
|
|
575 |
|
|
// 1-4. Test if aluout = 0
|
576 |
|
|
assign aluout_zero_node = (aluout == 0)?1:0;
|
577 |
|
|
|
578 |
|
|
// 1-5. Determine destination
|
579 |
|
|
always @(
|
580 |
|
|
inst_reg
|
581 |
|
|
or inst_movwf
|
582 |
|
|
or inst_bcf
|
583 |
|
|
or inst_bsf
|
584 |
|
|
or inst_clrf
|
585 |
|
|
or inst_movlw
|
586 |
|
|
or inst_addlw
|
587 |
|
|
or inst_sublw
|
588 |
|
|
or inst_andlw
|
589 |
|
|
or inst_iorlw
|
590 |
|
|
or inst_xorlw
|
591 |
|
|
or inst_retlw
|
592 |
|
|
or inst_clrw
|
593 |
|
|
or inst_movf
|
594 |
|
|
or inst_swapf
|
595 |
|
|
or inst_addwf
|
596 |
|
|
or inst_subwf
|
597 |
|
|
or inst_andwf
|
598 |
|
|
or inst_iorwf
|
599 |
|
|
or inst_xorwf
|
600 |
|
|
or inst_decf
|
601 |
|
|
or inst_incf
|
602 |
|
|
or inst_rlf
|
603 |
|
|
or inst_rrf
|
604 |
|
|
or inst_decfsz
|
605 |
|
|
or inst_incfsz
|
606 |
|
|
or inst_comf
|
607 |
|
|
)
|
608 |
|
|
begin
|
609 |
|
|
if (inst_movwf || inst_bcf || inst_bsf || inst_clrf)
|
610 |
|
|
begin
|
611 |
|
|
writew_node <= 0;
|
612 |
|
|
writeram_node <= 1;
|
613 |
|
|
end
|
614 |
|
|
else if ( inst_movlw || inst_addlw || inst_sublw || inst_andlw
|
615 |
|
|
|| inst_iorlw || inst_xorlw || inst_retlw || inst_clrw)
|
616 |
|
|
begin
|
617 |
|
|
writew_node <= 1;
|
618 |
|
|
writeram_node <= 0;
|
619 |
|
|
end
|
620 |
|
|
else if ( inst_movf || inst_swapf || inst_addwf || inst_subwf
|
621 |
|
|
|| inst_andwf || inst_iorwf || inst_xorwf || inst_decf
|
622 |
|
|
|| inst_incf || inst_rlf || inst_rrf || inst_decfsz
|
623 |
|
|
|| inst_incfsz || inst_comf)
|
624 |
|
|
begin
|
625 |
|
|
writew_node <= ~inst_reg[7]; // ("d" field of fetched instruction)
|
626 |
|
|
writeram_node <= inst_reg[7]; // ("d" field of fetched instruction)
|
627 |
|
|
end
|
628 |
|
|
else
|
629 |
|
|
begin
|
630 |
|
|
writew_node <= 0;
|
631 |
|
|
writeram_node <= 0;
|
632 |
|
|
end
|
633 |
|
|
end // End of determine destination logic
|
634 |
|
|
|
635 |
|
|
|
636 |
|
|
|
637 |
|
|
|
638 |
|
|
// 2-4-1. Calculation and store result into alu-output register
|
639 |
|
|
|
640 |
|
|
always @(
|
641 |
|
|
add_node
|
642 |
|
|
or aluinp1_reg
|
643 |
|
|
or aluinp2_reg
|
644 |
|
|
or status_reg
|
645 |
|
|
or inst_reg
|
646 |
|
|
or inst_movwf
|
647 |
|
|
or inst_bcf
|
648 |
|
|
or inst_bsf
|
649 |
|
|
or inst_btfsc
|
650 |
|
|
or inst_btfss
|
651 |
|
|
or inst_clrf
|
652 |
|
|
or inst_addlw
|
653 |
|
|
or inst_sublw
|
654 |
|
|
or inst_andlw
|
655 |
|
|
or inst_iorlw
|
656 |
|
|
or inst_xorlw
|
657 |
|
|
or inst_retlw
|
658 |
|
|
or inst_clrw
|
659 |
|
|
or inst_swapf
|
660 |
|
|
or inst_addwf
|
661 |
|
|
or inst_subwf
|
662 |
|
|
or inst_andwf
|
663 |
|
|
or inst_iorwf
|
664 |
|
|
or inst_xorwf
|
665 |
|
|
or inst_decf
|
666 |
|
|
or inst_incf
|
667 |
|
|
or inst_rlf
|
668 |
|
|
or inst_rrf
|
669 |
|
|
or inst_decfsz
|
670 |
|
|
or inst_incfsz
|
671 |
|
|
or inst_comf
|
672 |
|
|
)
|
673 |
|
|
begin
|
674 |
|
|
// 2-4-1-1. Set aluout register
|
675 |
|
|
// Rotate left
|
676 |
|
|
if (inst_rlf)
|
677 |
|
|
aluout <= {aluinp1_reg[6:0],status_reg[0]};
|
678 |
|
|
// Rotate right
|
679 |
|
|
else if (inst_rrf)
|
680 |
|
|
aluout <= {status_reg[0],aluinp1_reg[7:1]};
|
681 |
|
|
// Swap nibbles
|
682 |
|
|
else if (inst_swapf)
|
683 |
|
|
aluout <= {aluinp1_reg[3:0],aluinp1_reg[7:4]};
|
684 |
|
|
// Logical inversion
|
685 |
|
|
else if (inst_comf)
|
686 |
|
|
aluout <= ~aluinp1_reg;
|
687 |
|
|
// Logical AND, bit clear/bit test
|
688 |
|
|
else if ( inst_andlw || inst_andwf || inst_bcf || inst_btfsc
|
689 |
|
|
|| inst_btfss)
|
690 |
|
|
aluout <= (aluinp1_reg & aluinp2_reg);
|
691 |
|
|
// Logical OR, bit set
|
692 |
|
|
else if (inst_bsf || inst_iorlw || inst_iorwf)
|
693 |
|
|
aluout <= (aluinp1_reg | aluinp2_reg);
|
694 |
|
|
// Logical XOR
|
695 |
|
|
else if (inst_xorlw || inst_xorwf)
|
696 |
|
|
aluout <= (aluinp1_reg ^ aluinp2_reg);
|
697 |
|
|
// Addition, Subtraction, Increment, Decrement
|
698 |
|
|
else if ( inst_addlw || inst_addwf || inst_sublw || inst_subwf
|
699 |
|
|
|| inst_decf || inst_decfsz || inst_incf || inst_incfsz)
|
700 |
|
|
aluout <= add_node[7:0];
|
701 |
|
|
// Pass through
|
702 |
|
|
else aluout <= aluinp1_reg;
|
703 |
|
|
end
|
704 |
|
|
|
705 |
|
|
|
706 |
|
|
// MAIN EFSM: description of register value changes in each clock cycle
|
707 |
|
|
always @(posedge clk_i)
|
708 |
|
|
begin
|
709 |
|
|
// Assign reset (default) values of registers
|
710 |
|
|
if (reset_i)
|
711 |
|
|
begin
|
712 |
|
|
status_reg[7:5] <= 3'b0;
|
713 |
|
|
pclath_reg <= 0; // 0
|
714 |
|
|
intcon_reg[7:1] <= 7'b0;
|
715 |
|
|
aux_adr_lo_reg <= 0;
|
716 |
|
|
aux_adr_hi_reg <= 0;
|
717 |
|
|
ram_we_reg <= 0;
|
718 |
|
|
status_reg[4] <= 1; // /T0 = 1
|
719 |
|
|
status_reg[3] <= 1; // /PD = 1
|
720 |
|
|
stack_pnt_reg <= 0; // Reset stack pointer
|
721 |
|
|
end // End of reset assignments
|
722 |
|
|
else if (~exec_stall_reg && clk_en_i)
|
723 |
|
|
begin // Execution ceases during a stall cycle.
|
724 |
|
|
if (state_reg == Q2_PP) // 2-3. Q2 cycle
|
725 |
|
|
begin
|
726 |
|
|
// 2-3-1. Read data-RAM and store values to alu-input regs
|
727 |
|
|
// 2-3-1-1. Set aluinp1 register (source #1)
|
728 |
|
|
if ( inst_movf || inst_swapf || inst_addwf || inst_subwf
|
729 |
|
|
|| inst_andwf || inst_iorwf || inst_xorwf || inst_decf
|
730 |
|
|
|| inst_incf || inst_rlf || inst_rrf || inst_bcf
|
731 |
|
|
|| inst_bsf || inst_btfsc || inst_btfss || inst_decfsz
|
732 |
|
|
|| inst_incfsz || inst_comf)
|
733 |
|
|
|
734 |
|
|
aluinp1_reg <= ram_i_node; // RAM/Special registers
|
735 |
|
|
else
|
736 |
|
|
if ( inst_movlw || inst_addlw || inst_sublw || inst_andlw
|
737 |
|
|
|| inst_iorlw || inst_xorlw || inst_retlw)
|
738 |
|
|
aluinp1_reg <= inst_reg[7:0]; // Immediate value ("k")
|
739 |
|
|
else
|
740 |
|
|
if ( inst_clrf || inst_clrw) aluinp1_reg <= 0; // 0
|
741 |
|
|
else aluinp1_reg <= w_reg; // W register
|
742 |
|
|
|
743 |
|
|
// 2-3-1-2. Set aluinp2 register (source #2)
|
744 |
|
|
c_subtract_zero <= 0; // default to non-special case
|
745 |
|
|
c_dig_subtract_zero <= 0; // default to non-special case
|
746 |
|
|
if (inst_decf || inst_decfsz) aluinp2_reg <= -1; // for decr.
|
747 |
|
|
else if (inst_incf || inst_incfsz) aluinp2_reg <= 1; // for incr.
|
748 |
|
|
// -1 * W register (for subtract)
|
749 |
|
|
else if (inst_sublw || inst_subwf)
|
750 |
|
|
begin
|
751 |
|
|
aluinp2_reg <= ~w_reg + 1;
|
752 |
|
|
c_subtract_zero <= (w_reg == 0); // Indicate special case
|
753 |
|
|
c_dig_subtract_zero <= (w_reg[3:0] == 0); // Indicate special case
|
754 |
|
|
end
|
755 |
|
|
// operation of BCF: AND with inverted mask ("1..101..1")
|
756 |
|
|
// mask for BCF: value of only one position is 0
|
757 |
|
|
else if (inst_bcf) aluinp2_reg <= ~mask_node;
|
758 |
|
|
// operation of BSF: OR with mask_node ("0..010..0")
|
759 |
|
|
// operation of FSC and FSS: AND with mask_node, compare to 0
|
760 |
|
|
else if (inst_btfsc || inst_btfss || inst_bsf)
|
761 |
|
|
aluinp2_reg <= mask_node;
|
762 |
|
|
else aluinp2_reg <= w_reg; // W register
|
763 |
|
|
|
764 |
|
|
// 2-3-1-3. Set stack pointer register (pop stack)
|
765 |
|
|
if (inst_ret || inst_retlw || inst_retfie)
|
766 |
|
|
stack_pnt_reg <= stack_pnt_reg - 1; // cycles 3,2,1,0,7,6...
|
767 |
|
|
|
768 |
|
|
// 2-4-1-3. Set data-SRAM write enable (hazard-free)
|
769 |
|
|
// Set the write enables depending on the destination.
|
770 |
|
|
// (These have been implemented as registers to avoid glitches?
|
771 |
|
|
// It is not known to me (John Clayton) whether any glitches would
|
772 |
|
|
// really occur. It might be possible to generate these signals
|
773 |
|
|
// using combinational logic only, without using registers!
|
774 |
|
|
ram_we_reg <= (writeram_node && addr_sram);
|
775 |
|
|
aux_we_reg <= (writeram_node && addr_aux_dat);
|
776 |
|
|
end // End of Q2 state
|
777 |
|
|
|
778 |
|
|
//---------------------------------------------------------------------
|
779 |
|
|
|
780 |
|
|
else if (state_reg == QINT_PP) // Interrupt execution (instead of Q4_PP)
|
781 |
|
|
begin
|
782 |
|
|
// PORT-B0 INT
|
783 |
|
|
intcon_reg[1] <= 1; // set INTF
|
784 |
|
|
intcon_reg[7] <= 0; // clear GIE
|
785 |
|
|
stack_reg[stack_pnt_reg] <= old_pc_reg; // Push old PC
|
786 |
|
|
stack_pnt_reg <= stack_pnt_reg + 1; // increment stack pointer
|
787 |
|
|
// The old PC is pushed, so that the pre-empted instruction can be
|
788 |
|
|
// restarted later, when the retfie is executed.
|
789 |
|
|
end
|
790 |
|
|
|
791 |
|
|
//---------------------------------------------------------------------
|
792 |
|
|
|
793 |
|
|
else if (state_reg == Q4_PP) // Execution & writing of results.
|
794 |
|
|
begin
|
795 |
|
|
|
796 |
|
|
if (inst_call)
|
797 |
|
|
begin
|
798 |
|
|
stack_reg[stack_pnt_reg] <= pc_reg; // Push current PC
|
799 |
|
|
stack_pnt_reg <= stack_pnt_reg + 1; // increment stack pointer
|
800 |
|
|
end
|
801 |
|
|
|
802 |
|
|
if (inst_retfie) // "return from interrupt" instruction
|
803 |
|
|
begin
|
804 |
|
|
intcon_reg[7] <= 1; // Set GIE
|
805 |
|
|
end
|
806 |
|
|
|
807 |
|
|
// 2-4-1-2. Set C flag and DC flag
|
808 |
|
|
if (inst_addlw || inst_addwf || inst_sublw || inst_subwf)
|
809 |
|
|
begin
|
810 |
|
|
// c_dig_subtract_zero and c_subtract_zero are used to take care of the
|
811 |
|
|
// special case when subtracting zero, where the carry bit should be 1
|
812 |
|
|
// (meaning no borrow). It is explicitly set by these signals during
|
813 |
|
|
// that condition. See 16F84 datasheet, page 8 for further information
|
814 |
|
|
// about the C bit.
|
815 |
|
|
status_reg[1] <= addlow_node[4] || c_dig_subtract_zero; // DC flag
|
816 |
|
|
status_reg[0] <= add_node[8] || c_subtract_zero; // C flag
|
817 |
|
|
end
|
818 |
|
|
else if (inst_rlf) status_reg[0] <= aluinp1_reg[7]; // C flag
|
819 |
|
|
else if (inst_rrf) status_reg[0] <= aluinp1_reg[0]; // C flag
|
820 |
|
|
|
821 |
|
|
// 2-5-2. Store calculation result into destination,
|
822 |
|
|
// 2-5-2-1. Set W register
|
823 |
|
|
|
824 |
|
|
if (writew_node) w_reg <= aluout; // write W reg
|
825 |
|
|
|
826 |
4 |
jclaytons |
|
827 |
2 |
jclaytons |
// 2-5-2-2. Set data RAM/special registers,
|
828 |
|
|
if (writeram_node)
|
829 |
|
|
begin
|
830 |
|
|
if (addr_stat)
|
831 |
|
|
begin
|
832 |
|
|
status_reg[7:5] <= aluout[7:5]; // write IRP,RP1,RP0
|
833 |
|
|
// status(4),status(3)...unwritable, see below (/PD,/T0 part)
|
834 |
4 |
jclaytons |
if( ~disable_status_c ) status_reg[0] <= aluout[0]; // write C
|
835 |
|
|
if( ~disable_status_dc ) status_reg[1] <= aluout[1]; // write DC
|
836 |
2 |
jclaytons |
end
|
837 |
|
|
if (addr_fsr) fsr_reg <= aluout; // write FSR
|
838 |
|
|
if (addr_pclath) pclath_reg <= aluout[4:0]; // write PCLATH
|
839 |
|
|
if (addr_intcon) intcon_reg <= aluout; // write INTCON
|
840 |
|
|
if (addr_aux_adr_lo) aux_adr_lo_reg <= aluout; // write AUX low
|
841 |
|
|
if (addr_aux_adr_hi) aux_adr_hi_reg <= aluout; // write AUX high
|
842 |
|
|
end
|
843 |
|
|
|
844 |
|
|
// 2-5-2-3. Set/clear Z flag.
|
845 |
4 |
jclaytons |
if (addr_stat && ~disable_status_z) status_reg[2] <= aluout[2];
|
846 |
2 |
jclaytons |
else if ( inst_addlw || inst_addwf || inst_andlw || inst_andwf
|
847 |
|
|
|| inst_clrf || inst_clrw || inst_comf || inst_decf
|
848 |
|
|
|| inst_incf || inst_movf || inst_sublw || inst_subwf
|
849 |
|
|
|| inst_xorlw || inst_xorwf || inst_iorlw || inst_iorwf )
|
850 |
|
|
status_reg[2] <= aluout_zero_node; // Z=1 if result == 0
|
851 |
|
|
|
852 |
|
|
// 2-5-3. Clear RAM write enables (hazard-free)
|
853 |
|
|
ram_we_reg <= 0;
|
854 |
|
|
aux_we_reg <= 0;
|
855 |
|
|
|
856 |
|
|
end // End of Q4 state
|
857 |
|
|
end // End of "if (~exec_stall_reg)"
|
858 |
|
|
end // End of process
|
859 |
|
|
|
860 |
|
|
|
861 |
|
|
// Calculation of next processor state.
|
862 |
|
|
// (Not including reset conditions, which are covered by the clocked logic,
|
863 |
|
|
// which also includes a "global clock enable."
|
864 |
|
|
always @(
|
865 |
|
|
state_reg
|
866 |
|
|
or inst_sleep
|
867 |
|
|
or inte_sync_reg
|
868 |
|
|
or exec_stall_reg
|
869 |
|
|
or int_condition
|
870 |
|
|
)
|
871 |
|
|
begin
|
872 |
|
|
case (state_reg)
|
873 |
|
|
Q2_PP : if (int_condition) next_state_node <= QINT_PP;
|
874 |
|
|
else next_state_node <= Q4_PP;
|
875 |
|
|
Q4_PP : if (~exec_stall_reg && inst_sleep) next_state_node <= QSLEEP_PP;
|
876 |
|
|
else next_state_node <= Q2_PP;
|
877 |
|
|
QINT_PP : next_state_node <= Q2_PP;
|
878 |
|
|
QSLEEP_PP : if (inte_sync_reg) next_state_node <= Q2_PP;
|
879 |
|
|
else next_state_node <= QSLEEP_PP;
|
880 |
|
|
// Default condition provided for convention and completeness
|
881 |
|
|
// only. Logically, all of the conditions are already covered.
|
882 |
|
|
default : next_state_node <= Q2_PP;
|
883 |
|
|
endcase
|
884 |
|
|
end
|
885 |
|
|
|
886 |
|
|
|
887 |
|
|
// Clocked state transitions, based upon dataflow (non-clocked logic) in
|
888 |
|
|
// the previous always block.
|
889 |
|
|
always @(posedge clk_i)
|
890 |
|
|
begin
|
891 |
|
|
if (reset_i) state_reg <= Q2_PP;
|
892 |
|
|
else if (clk_en_i) state_reg <= next_state_node;
|
893 |
|
|
end // End of process
|
894 |
|
|
|
895 |
|
|
|
896 |
|
|
// Detect external interrupt requests
|
897 |
|
|
// You can code multiple interrupts if you wish, or use the single interrupt
|
898 |
|
|
// provided and simply have the interrupt service routine (ISR) check to find
|
899 |
|
|
// out the source of the interrupt, by or-ing together all of the interrupt
|
900 |
|
|
// sources and providing a readable register of their values at the time
|
901 |
|
|
// the interrupt occurred.
|
902 |
|
|
//
|
903 |
|
|
// When an interrupt is recognized by the processor, this is signified by
|
904 |
|
|
// entering "QINT_PP," which is treated like an executable instruction.
|
905 |
|
|
// The interrupt instruction can only be executed when not in a stall condition.
|
906 |
|
|
// It simply "pre-empts" the instruction that would have been executed during
|
907 |
|
|
// that cycle. Then, when retfie is executed, the pre-empted instruction is
|
908 |
|
|
// re-started (the stall cycle of the retfie is when the address of the
|
909 |
|
|
// instruction being re-started is fetched.)
|
910 |
|
|
//
|
911 |
|
|
// I was unable to obtain correct operation for capturing the negative edge,
|
912 |
|
|
// so I am discarding it. If one really needs to generate an interrupt on the
|
913 |
|
|
// falling edge, just use an inverted version of the signal (the inversion is
|
914 |
|
|
// often "free" inside of an FPGA anyhow.)
|
915 |
|
|
//
|
916 |
|
|
// Upon further testing, I discovered that even the rising edge "trigger" was not
|
917 |
|
|
// really truly an edge detection, it was more like a "set-reset" flip flop
|
918 |
|
|
// type of behavior. Rather than mess around with it any more, I am implementing
|
919 |
|
|
// a clocked "poor man's rising edge detector."
|
920 |
|
|
// Capture the rising edge of the interrupt input... This part is self clearing.
|
921 |
|
|
// It also means that the interrupt must last longer than one clock cycle in
|
922 |
|
|
// order to be properly recognized. (It is "pseudo edge triggered", not a true
|
923 |
|
|
// rising edge trigger.)
|
924 |
|
|
// When the interrupt is recognized, inte_sync_reg is cleared.
|
925 |
|
|
|
926 |
|
|
|
927 |
|
|
always @(posedge clk_i)
|
928 |
|
|
begin
|
929 |
|
|
if (clk_en_i) intrise_reg <= int0_i;
|
930 |
|
|
end // process
|
931 |
|
|
assign intrise = (int0_i && ~intrise_reg);
|
932 |
|
|
|
933 |
|
|
// The inte_sync_reg signal is used for waking up from SLEEP.
|
934 |
|
|
// (this flip flop is also a synchronizer to minimize the
|
935 |
|
|
// possibility of metastability due to changes at the input
|
936 |
|
|
// occurring at the same time as the processor clock edge...)
|
937 |
|
|
// It might be possible to eliminate this step, and issue the interrupt
|
938 |
|
|
// directly without this intermediate synchronizer flip-flop.
|
939 |
|
|
always @(posedge clk_i)
|
940 |
|
|
begin
|
941 |
|
|
if (reset_i || (state_reg == QINT_PP)) inte_sync_reg <= 0;
|
942 |
|
|
else if (clk_en_i && intrise && intcon_reg[4]) inte_sync_reg <= 1;
|
943 |
|
|
end
|
944 |
|
|
|
945 |
|
|
// Issue an interrupt when the interrupt is present.
|
946 |
|
|
// Also, do not issue an interrupt when there is a stall cycle coming!
|
947 |
4 |
jclaytons |
assign int_condition = (inte_sync_reg && ~exec_stall_reg && ~next_exec_stall && intcon_reg[7]);
|
948 |
2 |
jclaytons |
// Interrupt must be pending
|
949 |
|
|
// Next processor cycle must not be a stall
|
950 |
|
|
// GIE bit must be set to issue interrupt
|
951 |
|
|
|
952 |
|
|
// Circuit's output signals
|
953 |
|
|
assign prog_adr_o = pc_reg; // program ROM address
|
954 |
|
|
assign ram_adr_o = ram_adr_node; // data RAM address
|
955 |
|
|
assign ram_dat_o = aluout; // data RAM write data
|
956 |
|
|
assign ram_we_o = ram_we_reg; // data RAM write enable
|
957 |
|
|
|
958 |
|
|
assign aux_adr_o = {aux_adr_hi_reg,aux_adr_lo_reg};
|
959 |
|
|
assign aux_dat_io = (aux_we_reg && clk_en_i)?aluout:{8{1'bZ}};
|
960 |
|
|
assign aux_we_o = aux_we_reg;
|
961 |
|
|
|
962 |
|
|
endmodule
|
963 |
|
|
|
964 |
|
|
|
965 |
|
|
//`undef STATEBIT_SIZE
|