summaryrefslogtreecommitdiff
path: root/software/clic_interrupts/clic.c
blob: ccf98b29035faa87004252680e3b09562c07ecc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// See LICENSE for license details.

#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include <string.h>
#include "encoding.h"
#include <unistd.h>
#include "sifive/devices/clic.h"

#ifndef _SIFIVE_COREPLEXIP_ARTY_H
#error 'local_interrupts' demo only supported for Coreplex IP Eval Kits
#endif

// Global Variable used to show
// software interrupts.
volatile uint32_t g_debouncing;

void debounce();

static void clic_enable(int offset) __attribute__((noinline));
static void clic_enable(int offset)
{
  CLIC_REG8(CLIC_INTCFG + offset) = 0xff;
  CLIC_REG8(CLIC_INTIE  + offset) = 1;
}

static void clic_disable(int offset) __attribute__((noinline));
static void clic_disable(int offset)
{
  CLIC_REG8(CLIC_INTCFG + offset) = 0xff;
  CLIC_REG8(CLIC_INTIE  + offset) = 0;
}

// Structures for registering different interrupt handlers
// for different parts of the application.
typedef void (*interrupt_function_ptr_t) (void);

// This function enables some of the local interrupts sources
// used in this demo -- just those for the buttons and
// Switch 3.

void enable_local_interrupts(){
  clic_enable(IRQ_M_LOCAL + LOCAL_INT_SW_3);
  clic_enable(IRQ_M_LOCAL + LOCAL_INT_BTN_0);
  clic_enable(IRQ_M_LOCAL + LOCAL_INT_BTN_1);
  clic_enable(IRQ_M_LOCAL + LOCAL_INT_BTN_2);
  clic_enable(IRQ_M_LOCAL + LOCAL_INT_BTN_3);
}

void disable_local_interrupts() {
  clic_disable(IRQ_M_LOCAL + LOCAL_INT_SW_3);
  clic_disable(IRQ_M_LOCAL + LOCAL_INT_BTN_0);
  clic_disable(IRQ_M_LOCAL + LOCAL_INT_BTN_1);
  clic_disable(IRQ_M_LOCAL + LOCAL_INT_BTN_2);
  clic_disable(IRQ_M_LOCAL + LOCAL_INT_BTN_3);
}

/*Entry Point for Machine Software Interrupt Handler*/
void msi_isr() {
  const char msi_msg[] = "    Debouncing: (this message due to Software Interrupt)\n\n";
  write (STDOUT_FILENO, msi_msg, strlen(msi_msg));  

  //clear the  SW interrupt
  CLIC_REG(CLIC_MSIP) = 0;
}

/*Entry Point for Machine Timer Interrupt Handler*/
void mti_isr(){
  const char mti_msg[] = "    Timer interrupt, done debouncing.\n\n";
  write (STDOUT_FILENO, mti_msg, strlen(mti_msg));  

  // Disable the timer interrupt. The Debounce logic sets it.
  clic_disable(IRQ_M_TIMER);

  // Enable all the local interrupts
  enable_local_interrupts();
}

const char * instructions_msg = " \
\n\
                         SiFive, Inc\n\
 E21 Core IP Eval Kit 'clic_interrupts' demo.\n\
\n\
The Buttons 0-3 and Switch 3 are enabled as local\n\
interrupts sources. A .5 s 'debounce' timer is used \n\
between these interrupts. Software interrupts are\n\
used to print a message while debouncing.\n\
Note the priority of the interrupts sources.\n\
\n";

void print_instructions() {

  write (STDERR_FILENO, instructions_msg, strlen(instructions_msg));

}

void button_0_isr(void) {

  // Toggle Red LED
  const char button_0_msg[] = "Button 0 was pressed. Toggle Red.\n";
  write (STDOUT_FILENO, button_0_msg, strlen(button_0_msg));  
  GPIO_REG(GPIO_OUTPUT_VAL) = GPIO_REG(GPIO_OUTPUT_VAL) ^ (0x1 << RED_LED_OFFSET);
  debounce();  
};

void button_1_isr(void) {

  // Toggle Green LED
  const char button_1_msg[] = "Button 1 was pressed. Toggle Green.\n";
  write (STDOUT_FILENO, button_1_msg, strlen(button_1_msg));  
  GPIO_REG(GPIO_OUTPUT_VAL) = GPIO_REG(GPIO_OUTPUT_VAL) ^ (0x1 << GREEN_LED_OFFSET);
  debounce();
};


void button_2_isr(void) {

  // Toggle Blue LED
  const char button_2_msg[] = "Button 2 was pressed. Toggle Blue.\n";
  write (STDOUT_FILENO, button_2_msg, strlen(button_2_msg));  
  GPIO_REG(GPIO_OUTPUT_VAL) = GPIO_REG(GPIO_OUTPUT_VAL) ^ (0x1 << BLUE_LED_OFFSET);
  debounce();
  
};

void button_3_isr(void) {
  const char button_3_msg[] = "Button 3 was pressed! (No LEDs change).\n";
  write (STDOUT_FILENO, button_3_msg, strlen(button_3_msg));
  debounce();
}

void switch_3_isr(void) {
  const char sw_3_msg[] = "Switch 3 is on! But buttons have higher priority.\n";
  write (STDOUT_FILENO, sw_3_msg, strlen(sw_3_msg));
  debounce();
}

void debounce(int local_interrupt_num) {
  // Disable the most recent interrupt.
  // Don't enable it again until the timer goes off,
  // in .5 second.
  
  // Set the machine timer to go off in .5 seconds.
  // If the timer was already set to go off, this "cancels"
  // the current one.
  
  volatile uint64_t * mtime       = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
  volatile uint64_t * mtimecmp    = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
  uint64_t now = *mtime;
  uint64_t then = now + .5*RTC_FREQ;
  *mtimecmp = then;

  disable_local_interrupts();
  g_debouncing = 1;

  // Enable the Machine-Timer bit in MIE
  clic_enable(IRQ_M_TIMER);
}

// See bsp/env/<BOARD>/init.c for how this
// interrupt vector is used.

interrupt_function_ptr_t localISR[32]; 

static void unmapped_interrupt(void) {
  printf("unmapped interrupt");
  asm volatile ("1: j 1b" ::: "memory");
}

int main(int argc, char **argv)
{
  // Configure LEDs as outputs.
  GPIO_REG(GPIO_INPUT_EN)    &= ~((0x1<< RED_LED_OFFSET) | (0x1<< GREEN_LED_OFFSET) | (0x1 << BLUE_LED_OFFSET)) ;
  GPIO_REG(GPIO_OUTPUT_EN)   |=  ((0x1<< RED_LED_OFFSET)| (0x1<< GREEN_LED_OFFSET) | (0x1 << BLUE_LED_OFFSET)) ;
  GPIO_REG(GPIO_OUTPUT_VAL)  |=   (0x1 << BLUE_LED_OFFSET) ;
  GPIO_REG(GPIO_OUTPUT_VAL)  &=  ~((0x1<< RED_LED_OFFSET) | (0x1<< GREEN_LED_OFFSET)) ;

  // The Buttons and Switches which are used as local interrupt sources
  // do not go through the GPIO peripheral, so they do not need to
  // be configured as inputs.
  
  // Disable the timer & local interrupts until setup is done (they're
  // not reset by default)
  
  // Unconfigure the CLIC before doing anything
  for (int i = 0; i < 1024; ++i)
    CLIC_REG8(CLIC_INTIE + i) = 0;

  // Write down the software interrupt pending bit, as we shouldn't start out
  // by debouncing anything at all.
  CLIC_REG(CLIC_MSIP) = 0;

  for (int isr = 0; isr < 32; isr++)
    localISR[isr] = &unmapped_interrupt;
  
  localISR[IRQ_M_SOFT]  = msi_isr;
  localISR[IRQ_M_TIMER] = mti_isr;
  localISR[IRQ_M_LOCAL + LOCAL_INT_SW_3]  = switch_3_isr;
  localISR[IRQ_M_LOCAL + LOCAL_INT_BTN_0] = button_0_isr;
  localISR[IRQ_M_LOCAL + LOCAL_INT_BTN_1] = button_1_isr;
  localISR[IRQ_M_LOCAL + LOCAL_INT_BTN_2] = button_2_isr;
  localISR[IRQ_M_LOCAL + LOCAL_INT_BTN_3] = button_3_isr;

  // Set up the CLIC interrupt mechanism
  CLIC_REG(CLIC_CFG) = 4<<1; // nmBits=0; nlBits=4; nvBits=0

  print_instructions();
  
  enable_local_interrupts();

  g_debouncing = 0;

  // Enable SW interrupts as well in this demo.
  clic_enable(IRQ_M_SOFT);
  
  // Enable all global interrupts
  set_csr(mstatus, MSTATUS_MIE);

  volatile int foo = 1;
  while(foo){
    if (g_debouncing){
      //Trigger a SW interrupt
      CLIC_REG(CLIC_MSIP) = 1;
      g_debouncing = 0;
    }
  }

  return 0;

}