1 /* Blackfin Trace (TBUF) model.
2 
3    Copyright (C) 2010-2011 Free Software Foundation, Inc.
4    Contributed by Analog Devices, Inc.
5 
6    This file is part of simulators.
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 
23 #include "sim-main.h"
24 #include "devices.h"
25 #include "dv-bfin_cec.h"
26 #include "dv-bfin_trace.h"
27 
28 /* Note: The circular buffering here might look a little buggy wrt mid-reads
29          and consuming the top entry, but this is simulating hardware behavior.
30          The hardware is simple, dumb, and fast.  Don't write dumb Blackfin
31          software and you won't have a problem.  */
32 
33 /* The hardware is limited to 16 entries and defines TBUFCTL.  Let's extend it ;).  */
34 #ifndef SIM_BFIN_TRACE_DEPTH
35 #define SIM_BFIN_TRACE_DEPTH 6
36 #endif
37 #define SIM_BFIN_TRACE_LEN (1 << SIM_BFIN_TRACE_DEPTH)
38 #define SIM_BFIN_TRACE_LEN_MASK (SIM_BFIN_TRACE_LEN - 1)
39 
40 struct bfin_trace_entry
41 {
42   bu32 src, dst;
43 };
44 struct bfin_trace
45 {
46   bu32 base;
47   struct bfin_trace_entry buffer[SIM_BFIN_TRACE_LEN];
48   int top, bottom;
49   bool mid;
50 
51   /* Order after here is important -- matches hardware MMR layout.  */
52   bu32 tbufctl, tbufstat;
53   char _pad[0x100 - 0x8];
54   bu32 tbuf;
55 };
56 #define mmr_base()      offsetof(struct bfin_trace, tbufctl)
57 #define mmr_offset(mmr) (offsetof(struct bfin_trace, mmr) - mmr_base())
58 
59 static const char * const mmr_names[] =
60 {
61   "TBUFCTL", "TBUFSTAT", [mmr_offset (tbuf) / 4] = "TBUF",
62 };
63 #define mmr_name(off) (mmr_names[(off) / 4] ? : "<INV>")
64 
65 /* Ugh, circular buffers.  */
66 #define TBUF_LEN(t) ((t)->top - (t)->bottom)
67 #define TBUF_IDX(i) ((i) & SIM_BFIN_TRACE_LEN_MASK)
68 /* TOP is the next slot to fill.  */
69 #define TBUF_TOP(t) (&(t)->buffer[TBUF_IDX ((t)->top)])
70 /* LAST is the latest valid slot.  */
71 #define TBUF_LAST(t) (&(t)->buffer[TBUF_IDX ((t)->top - 1)])
72 /* LAST_LAST is the second-to-last valid slot.  */
73 #define TBUF_LAST_LAST(t) (&(t)->buffer[TBUF_IDX ((t)->top - 2)])
74 
75 static unsigned
bfin_trace_io_write_buffer(struct hw * me,const void * source,int space,address_word addr,unsigned nr_bytes)76 bfin_trace_io_write_buffer (struct hw *me, const void *source,
77 			    int space, address_word addr, unsigned nr_bytes)
78 {
79   struct bfin_trace *trace = hw_data (me);
80   bu32 mmr_off;
81   bu32 value;
82 
83   value = dv_load_4 (source);
84   mmr_off = addr - trace->base;
85 
86   HW_TRACE_WRITE ();
87 
88   switch (mmr_off)
89     {
90     case mmr_offset(tbufctl):
91       trace->tbufctl = value;
92       break;
93     case mmr_offset(tbufstat):
94     case mmr_offset(tbuf):
95       /* Discard writes to these.  */
96       break;
97     default:
98       dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
99       break;
100     }
101 
102   return nr_bytes;
103 }
104 
105 static unsigned
bfin_trace_io_read_buffer(struct hw * me,void * dest,int space,address_word addr,unsigned nr_bytes)106 bfin_trace_io_read_buffer (struct hw *me, void *dest,
107 			   int space, address_word addr, unsigned nr_bytes)
108 {
109   struct bfin_trace *trace = hw_data (me);
110   bu32 mmr_off;
111   bu32 value;
112 
113   mmr_off = addr - trace->base;
114 
115   HW_TRACE_READ ();
116 
117   switch (mmr_off)
118     {
119     case mmr_offset(tbufctl):
120       value = trace->tbufctl;
121       break;
122     case mmr_offset(tbufstat):
123       /* Hardware is limited to 16 entries, so to stay compatible with
124          software, limit the value to 16.  For software algorithms that
125          keep reading while (TBUFSTAT != 0), they'll get all of it.  */
126       value = MIN (TBUF_LEN (trace), 16);
127       break;
128     case mmr_offset(tbuf):
129       {
130 	struct bfin_trace_entry *e;
131 
132 	if (TBUF_LEN (trace) == 0)
133 	  {
134 	    value = 0;
135 	    break;
136 	  }
137 
138 	e = TBUF_LAST (trace);
139 	if (trace->mid)
140 	  {
141 	    value = e->src;
142 	    --trace->top;
143 	  }
144 	else
145 	  value = e->dst;
146 	trace->mid = !trace->mid;
147 
148 	break;
149       }
150     default:
151       while (1) /* Core MMRs -> exception -> doesn't return.  */
152 	dv_bfin_mmr_invalid (me, addr, nr_bytes, false);
153       break;
154     }
155 
156   dv_store_4 (dest, value);
157 
158   return nr_bytes;
159 }
160 
161 static void
attach_bfin_trace_regs(struct hw * me,struct bfin_trace * trace)162 attach_bfin_trace_regs (struct hw *me, struct bfin_trace *trace)
163 {
164   address_word attach_address;
165   int attach_space;
166   unsigned attach_size;
167   reg_property_spec reg;
168 
169   if (hw_find_property (me, "reg") == NULL)
170     hw_abort (me, "Missing \"reg\" property");
171 
172   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
173     hw_abort (me, "\"reg\" property must contain three addr/size entries");
174 
175   hw_unit_address_to_attach_address (hw_parent (me),
176 				     &reg.address,
177 				     &attach_space, &attach_address, me);
178   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
179 
180   if (attach_size != BFIN_COREMMR_TRACE_SIZE)
181     hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_TRACE_SIZE);
182 
183   hw_attach_address (hw_parent (me),
184 		     0, attach_space, attach_address, attach_size, me);
185 
186   trace->base = attach_address;
187 }
188 
189 static void
bfin_trace_finish(struct hw * me)190 bfin_trace_finish (struct hw *me)
191 {
192   struct bfin_trace *trace;
193 
194   trace = HW_ZALLOC (me, struct bfin_trace);
195 
196   set_hw_data (me, trace);
197   set_hw_io_read_buffer (me, bfin_trace_io_read_buffer);
198   set_hw_io_write_buffer (me, bfin_trace_io_write_buffer);
199 
200   attach_bfin_trace_regs (me, trace);
201 }
202 
203 const struct hw_descriptor dv_bfin_trace_descriptor[] =
204 {
205   {"bfin_trace", bfin_trace_finish,},
206   {NULL, NULL},
207 };
208 
209 #define TRACE_STATE(cpu) DV_STATE_CACHED (cpu, trace)
210 
211 /* This is not re-entrant, but neither is the cpu state, so this shouldn't
212    be a big deal ...  */
bfin_trace_queue(SIM_CPU * cpu,bu32 src_pc,bu32 dst_pc,int hwloop)213 void bfin_trace_queue (SIM_CPU *cpu, bu32 src_pc, bu32 dst_pc, int hwloop)
214 {
215   struct bfin_trace *trace = TRACE_STATE (cpu);
216   struct bfin_trace_entry *e;
217   int len, ivg;
218 
219   /* Only queue if powered.  */
220   if (!(trace->tbufctl & TBUFPWR))
221     return;
222 
223   /* Only queue if enabled.  */
224   if (!(trace->tbufctl & TBUFEN))
225     return;
226 
227   /* Ignore hardware loops.
228      XXX: This is what the hardware does, but an option to ignore
229      could be useful for debugging ...  */
230   if (hwloop >= 0)
231     return;
232 
233   /* Only queue if at right level.  */
234   ivg = cec_get_ivg (cpu);
235   if (ivg == IVG_RST)
236     /* XXX: This is what the hardware does, but an option to ignore
237             could be useful for debugging ...  */
238     return;
239   if (ivg <= IVG_EVX && (trace->tbufctl & TBUFOVF))
240     /* XXX: This is what the hardware does, but an option to ignore
241             could be useful for debugging ... just don't throw an
242             exception when full and in EVT{0..3}.  */
243     return;
244 
245   /* Are we full ?  */
246   len = TBUF_LEN (trace);
247   if (len == SIM_BFIN_TRACE_LEN)
248     {
249       if (trace->tbufctl & TBUFOVF)
250 	{
251 	  cec_exception (cpu, VEC_OVFLOW);
252 	  return;
253 	}
254 
255       /* Overwrite next entry.  */
256       ++trace->bottom;
257     }
258 
259   /* One level compression.  */
260   if (len >= 1 && (trace->tbufctl & TBUFCMPLP))
261     {
262       e = TBUF_LAST (trace);
263       if (src_pc == (e->src & ~1) && dst_pc == (e->dst & ~1))
264 	{
265 	  /* Hardware sets LSB when level is compressed.  */
266 	  e->dst |= 1;
267 	  return;
268 	}
269     }
270 
271   /* Two level compression.  */
272   if (len >= 2 && (trace->tbufctl & TBUFCMPLP_DOUBLE))
273     {
274       e = TBUF_LAST_LAST (trace);
275       if (src_pc == (e->src & ~1) && dst_pc == (e->dst & ~1))
276 	{
277 	  /* Hardware sets LSB when level is compressed.  */
278 	  e->src |= 1;
279 	  return;
280 	}
281     }
282 
283   e = TBUF_TOP (trace);
284   e->dst = dst_pc;
285   e->src = src_pc;
286   ++trace->top;
287 }
288