1<?
2/**
3 * Register Class: display pretty pictures of registers
4 *
5 * @license   GPL-3
6 * @author    Mike Frysinger <vapier@gentoo.org>
7 */
8
9/* Register Diagram Conventions (from BF537 HRM)
10 *  Register diagrams use the following conventions:
11 * The descriptive name of the register appears at the top, followed by
12 *  the short form of the name in parentheses (see TableP-1).
13 * If the register is read-only (RO), write-1-to-set (W1S), or
14 *  write-1-to-clear (W1C), this information appears under the name.
15 *  Read/write is the default and is not noted. Additional descriptive
16 *  text may follow.
17 * If any bits in the register do not follow the overall read/write con-
18 *  vention, this is noted in the bit description after the bit name.
19 * If a bit has a short name, the short name appears first in the bit
20 *  description, followed by the long name in parentheses.
21 * The reset value appears in binary in the individual bits and in hexa-
22 *  decimal to the right of the register.
23 * Bits marked x have an unknown reset value. Consequently, the
24 *  reset value of registers that contain such bits is undefined or depen-
25 *  dent on pin values at reset.
26 * Shaded bits are reserved.
27 *  To ensure upward compatibility with future implementations,
28 *  write back the value that is read for reserved bits in a register,
29 *  unless otherwise specified.
30 */
31
32/* TODO:
33 *	x handle bit ranges that split across the 16bit boundary (WDOG_CNT)
34 *	x register sub captions (RTC_ISTAT)
35 *	- partial undefines (RTC_ICTL)
36 *	- long bit descriptions may overlap vertical lines
37 *	- SUB_LABELS: bit name explanations (PLL_CTL -> MSEL -> Multiplier Select)
38 *	x try and balance descriptions better rather than by bit pos (PLL_CTL)
39 *	- if register display does not span multiple banks, only show Reset on right
40 *  - try and get a better size estimate instead of starting out with 1x1
41 * WISHLIST:
42 *	- add support for arbitrary bit fields (more than just registers)
43 */
44
45define("W1C", 0x1);
46class bit {
47	var $start, $end, $name, $desc, $flags;
48	public function bit($data)
49	{
50		$data = array_pad($data, 5, 0);
51		$this->start = $data[0];
52		$this->end   = $data[1];
53		$this->name  = $data[2];
54		$this->desc  = $data[3];
55		if (gettype($data[4]) == "string") {
56			$flags = explode(" ", $data[4]);
57			foreach ($flags as $f)
58				switch ($f) {
59				case "W1C": $this->flags |= W1C; break;
60				}
61		} else
62			$this->flags = $data[4];
63	}
64	public function bit_range($bit_high = -1, $bit_range = -1)
65	{
66		if ($bit_high != -1 && $this->start != $this->end)
67			return array(min($this->start, $bit_high), max($this->end, $bit_high - $bit_range + 1));
68		else
69			return array($this->start, $this->end);
70	}
71	public function format_name($bit_high = -1, $bit_range = -1)
72	{
73		$ret = $this->name;
74		if ($this->start != $this->end) {
75			$range = $this->bit_range($bit_high, $bit_range);
76			$bit_start = $range[0] - $this->end;
77			$bit_end = $range[1] - $this->end;
78			$ret .= "[" . $bit_start . ":" . $bit_end . "]";
79		}
80		if ($this->flags & W1C)
81			$ret .= " (W1C)";
82		return $ret;
83	}
84}
85
86define("FONT_TITLE", 0);
87define("FONT_BIT_LABELS", 1);
88define("FONT_BITS", 2);
89define("FONT_LABELS", 3);
90define("FONT_DESC", 4);
91class im {
92	private $im;
93	public $grey, $white, $black;
94	public function im($max_x = 1, $max_y = 1)
95	{
96		/* create a transparent image */
97		$im = imagecreatetruecolor($max_x, $max_y);
98		imagesavealpha($im, true);
99		$trans = imagecolorallocatealpha($im, 0, 0, 0, 127);
100		imagefill($im, 0, 0, $trans);
101
102		$this->grey = imagecolorallocate($im, 210, 210, 210);
103		$this->white = imagecolorallocate($im, 255, 255, 255);
104		$this->black = imagecolorallocate($im, 0, 0, 0);
105
106		$this->im = $im;
107		return $im;
108	}
109	public function enlarge($newsx, $newsy)
110	{
111		/* make sure image area is large enough */
112		$oldsx = imagesx($this->im);
113		$oldsy = imagesy($this->im);
114		if ($newsx < $oldsx && $newsy < $oldsy)
115			return;
116
117		$newsx = max($newsx, $oldsx);
118		$newsy = max($newsy, $oldsy);
119		//print_r(debug_backtrace());
120		//echo "enlarging image from ${oldsx}x${oldsy} to ${newsx}x${newsy}\n";
121		$oldim = $this->im;
122		$this->im($newsx, $newsy);
123		imagecopy($this->im, $oldim, 0, 0, 0, 0, $oldsx, $oldsy);
124		imagedestroy($oldim);
125	}
126	public function output($filename)
127	{
128		return imagepng($this->im, $filename, 9);
129	}
130	public function destroy()
131	{
132		return imagedestroy($this->im);
133	}
134
135	public function rect($x, $y, $w, $h, $col, $fill = "")
136	{
137		$this->enlarge($w + 1, $h + 1);
138		$this->enlarge($x + 1, $y + 1);
139		if ($fill == "")
140			imagerectangle($this->im, $x, $y, $w, $h, $col);
141		else
142			imagefilledrectangle($this->im, $x, $y, $w, $h, $col);
143		//echo "rect($x, $y, $w, $h, ...);\n";
144		//$this->text($x, $y, FONT_BIT_LABELS, "$x");
145	}
146	public function line($x1, $y1, $x2, $y2)
147	{
148		$this->enlarge(max($x1, $x2) + 1, max($y1, $y2) + 1);
149		imageline($this->im, $x1, $y1, $x2, $y2, $this->black);
150	}
151
152	private $text_range = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-+^()[]{}";
153	private $font_path = "/usr/share/fonts/ttf-bitstream-vera";
154	private $font_sizes = array(15, 10, 10, 12, 10);
155	/* mono: VeraMono mono_bold: VeraMoBd sans_bold: VeraBd serif: Vera */
156	private $font_files = array("VeraBd", "VeraMono", "VeraMoBd", "VeraMoBd", "Vera");
157
158	private function font_path($fontidx)
159	{
160		return $this->font_path . "/" . $this->font_files[$fontidx] . ".ttf";
161	}
162	private function font_dims($fontidx, $text, $angle = 0)
163	{
164		return imagettfbbox($this->font_sizes[$fontidx], $angle, $this->font_path($fontidx), $text);
165	}
166	private function _font_height(array $box) { return $box[1] - $box[7]; }
167	public function font_height($fontidx, $text = "", $angle = 0)
168	{
169		if ($text == "")
170			$text = $this->text_range;
171		return $this->_font_height($this->font_dims($fontidx, $text, $angle));
172	}
173	private function _font_width(array $box) { return $box[2] - $box[0]; }
174	public function font_width($fontidx, $text, $angle = 0)
175	{
176		return $this->_font_width($this->font_dims($fontidx, $text, $angle));
177	}
178	private function _text($x, $y, $fontidx, $text, $height_text, $angle = 0)
179	{
180		$fh = $this->font_height($fontidx, $height_text, $angle);
181		$fw = $this->font_width($fontidx, " $text ", $angle);
182
183		$this->enlarge($x + $fw, $y + $fh * 1.5);
184
185		return imagettftext($this->im, $this->font_sizes[$fontidx], $angle, $x,
186				$y + $fh, $this->black, $this->font_path($fontidx), $text);
187	}
188	public function exact_text($x, $y, $fontidx, $text, $angle = 0)
189	{
190		return $this->_text($x, $y, $fontidx, $text, $text, $angle);
191	}
192	public function text($x, $y, $fontidx, $text, $angle = 0)
193	{
194		return $this->_text($x, $y, $fontidx, $text, "", $angle);
195	}
196}
197
198class register {
199	var $name, $desc, $mmr_addr, $maxbits, $bits, $resetval, $bitrange, $perms, $sub_desc;
200	public function register($name, $desc, $mmr_addr, $resetval, $maxbits, $perms, $sub_desc, $bits)
201	{
202		$this->name = $name;
203		$this->desc = $desc;
204		$this->mmr_addr = $mmr_addr + 0; /* force conversion to int */
205		if ($resetval !== "undef")
206			$resetval += 0;
207		$this->resetval = $resetval;
208		$this->perms = $perms;
209		$this->sub_desc = $sub_desc;
210		$this->bits = array();
211		$this->maxbits = $maxbits;
212		foreach ($bits as $bit) {
213			$b = new bit($bit);
214			//print_r($b);
215			$this->bits[$b->start] = $b;
216		}
217		$this->bitrange = 16;
218	}
219	function bit_find($bit)
220	{
221		foreach ($this->bits as $b) {
222			if ($bit <= $b->start && $bit >= $b->end)
223				return $b;
224		}
225		return false;
226	}
227	function bit_defined($bit)
228	{
229		return ($this->bit_find($bit) != false);
230	}
231
232	private function bitpos($bit, $bitdim, $bitmax)
233	{
234		$inv = abs($bit - $bitmax);
235		return ($inv * $bitdim) + floor($inv / 4) + 1;
236	}
237	private function bitrange($bits, $upper, $lower)
238	{
239		$upper++; $lower++; /* they ask for the 15th bit so we want to shift 16 ... */
240		return ($bits & ((1 << $upper) - 1) ^ ((1 << $lower) - 1)) >> $lower;
241	}
242public function render($output_file) {
243	$register = $this;
244
245	/* setup some image stuff to play with */
246	$im = new im();
247	$bitdim = $im->font_height(FONT_BITS, "01X") * 3;
248	$x = 0;
249	$y = 0;
250
251	/* draw register description */
252	$text = $register->name;
253	if ($register->desc !== "")
254		$text .= ": " . $register->desc;
255	if ($register->perms !== "")
256		$text .= " - " . $register->perms;
257	$im->text($x, $y, FONT_TITLE, $text);
258	$ymin = $im->font_height(FONT_TITLE);
259
260	/* draw register sub desc if applicable */
261	if ($register->sub_desc !== "") {
262		$im->text($x, $y + $ymin, FONT_DESC, " ".$register->sub_desc);
263		$ymin *= 2;
264	} else
265		$ymin *= 1.5;
266
267	/* draw MMR address and complete reset desc */
268	if ($register->mmr_addr === "sysreg")
269		$mmr_disp = sprintf("System Register");
270	else if ($register->mmr_addr === 0)
271		$mmr_disp = "";
272	else
273		$mmr_disp = sprintf("MMR = 0x%08X", $register->mmr_addr);
274	$reset_disp = sprintf("Reset = 0x%0" . ($register->maxbits / 4) . "X", $register->resetval);
275
276	$mmrsx = $im->font_width(FONT_LABELS, $mmr_disp);
277	$resetsx = $im->font_width(FONT_LABELS, $reset_disp);
278	$xmin = max($mmrsx, $resetsx);
279
280	$y = $ymin;
281	$im->text($xmin - $mmrsx, $y, FONT_LABELS, $mmr_disp);
282	if ($register->resetval !== "undef") {
283		$y += $im->font_height(FONT_LABELS);
284		$im->text($xmin - $resetsx, $y, FONT_LABELS, $reset_disp);
285	}
286
287	$xmin += $bitdim;
288	$ymin += $bitdim;
289
290	/* find the largest desc text string so we dont underflow the image */
291	foreach ($register->bits as $bit)
292		$xmin = max($xmin, $im->font_width(FONT_LABELS, $bit->format_name()) + $bitdim);
293
294	/* break the register up into groups of 16 bits */
295for ($bitset = $register->maxbits; $bitset > 0; $bitset -= $register->bitrange) {
296	$bitset_h = $bitset - 1;
297	$bitset_l = $bitset - $register->bitrange - 1;
298	$bitset_m = ($bitset_h - $bitset_l) / 2 + $bitset_l;
299//echo "Processing bitset $bitset_l . $bitset_m . $bitset_h\n";
300
301	/* balance the labels between the left and right */
302	$bitset_m_set = array();
303	for ($b = $bitset_h; $b > $bitset_l; $b--) {
304		$bit = $register->bit_find($b);
305		if ($bit == false)
306			continue;
307		$range = $bit->bit_range($bitset_h, $register->bitrange);
308		array_push($bitset_m_set, $b);
309		$b -= ($range[0] - $range[1]);
310	}
311	if (count($bitset_m_set) == 0)
312		echo "BAD!!: bitset_m_set is 0!\n";
313	else
314		$bitset_m = $bitset_m_set[count($bitset_m_set) / 2];
315//echo " adjusted mid to [".count($bitset_m_set)."/2] $bitset_m\n";
316
317	/* first draw the register boxes */
318	$x = $register->bitpos($bitset_h, $bitdim, $bitset_h) + $xmin - 1;
319	$y = $ymin;
320	$im->line($x, $y-$bitdim*0.2, $x, $y+$bitdim*1.2);
321	$im->line($x+1, $y-$bitdim*0.2, $x+1, $y+$bitdim*1.2);
322	for ($b = $bitset_h; $b > $bitset_l; $b--) {
323		$x = $register->bitpos($b, $bitdim, $bitset_h) + $xmin;
324		$bc = ($register->bit_defined($b) ? $im->white : $im->grey);
325		$im->rect($x, $y, $x+$bitdim, $y+$bitdim, $bc, "fill");
326		$im->rect($x, $y, $x+$bitdim, $y+$bitdim, $im->black);
327		/* draw a slightly thicker verticle line in between nibbles */
328		if ($b % 4 == 0) {
329			$im->line($x+$bitdim, $y-$bitdim*0.2, $x+$bitdim, $y+$bitdim*1.2);
330			$im->line($x+$bitdim+1, $y-$bitdim*0.2, $x+$bitdim+1, $y+$bitdim*1.2);
331		}
332
333		/* draw the bit pos marker */
334		$fw = $im->font_width(FONT_BITS, $b);
335		$xoff = ($bitdim - $fw) / 2;
336		$yoff = $im->font_height(FONT_BITS) * 1.25;
337		$im->text($x+$xoff, $y-$yoff, FONT_BITS, $b);
338
339		/* draw the default bit value */
340		if ($register->resetval === "undef")
341			$bit_disp = "x";
342		else
343			$bit_disp = ($register->resetval & (0x1 << $b) ? "1" : "0");
344		$xoff = ($bitdim - $im->font_width(FONT_BIT_LABELS, $bit_disp)) / 2;
345		$yoff = ($bitdim - $im->font_height(FONT_BIT_LABELS, $bit_disp)) / 2;
346		$im->exact_text($x+$xoff, $y+$yoff, FONT_BIT_LABELS, $bit_disp);
347	}
348
349	/* draw the partial reset value to the right of this set of bits */
350	$x = $register->bitpos($bitset_l, $bitdim, $bitset_h) + $xmin;
351	$bit_disp = "Reset = ";
352	if ($register->resetval === "undef")
353		$bit_disp .= "undefined";
354	else
355		$bit_disp .= sprintf("0x%04X", $this->bitrange($register->resetval, $bitset_h, $bitset_l));
356	$yoff = ($bitdim - $im->font_height(FONT_BIT_LABELS, $bit_disp)) / 2;
357	$im->exact_text($x+$xoff, $y+$yoff-1, FONT_LABELS, $bit_disp);
358
359	/* now draw the lines underneath -- first the left, then the right */
360	$x = $xmin;
361	$y = $ymin + $bitdim * 1.2;
362	$xoff = $bitdim / 2;
363	$yadd = $bitdim * 3;
364	for ($i = 0; $i < 2; ++$i) {
365		if ($i == 0) {
366			$b_start = $bitset_h;
367			$b_end = $bitset_m;
368			$b_inc = -1;
369
370			/* precalc the left width so all text is aligned */
371			$desc_adjust = 0;
372			for ($b = $b_start; $b*$b_inc < $b_end*$b_inc; $b += $b_inc) {
373				$bit = $register->bit_find($b);
374				if ($bit == false)
375					continue;
376				$text = $bit->format_name($bitset_h, $register->bitrange);
377				$desc_adjust = max($desc_adjust, $im->font_width(FONT_LABELS, $text." "));
378				$b += ($bit->start - $bit->end) * $b_inc;
379			}
380		} else {
381			$b_start = $bitset_l + 1;
382			$b_end = $bitset_m + 1;
383			$b_inc = 1;
384			$desc_adjust = $im->font_width(FONT_LABELS, " ");
385		}
386		$num_def = 0;
387		$yoff = $bitdim / 2;
388
389//echo "--- $i $b_start $b_end $b_inc\n";
390	for ($b = $b_start; $b*$b_inc < $b_end*$b_inc; $b += $b_inc) {
391		$bit = $register->bit_find($b);
392//echo "Looking for bit $b: " . ($bit == false ? "no" : "yes") . "\n";
393		if ($bit == false)
394			continue;
395
396		$text = $bit->format_name($bitset_h, $register->bitrange);
397		$range = $bit->bit_range($bitset_h, $register->bitrange);
398		$x = $register->bitpos($range[0], $bitdim, $bitset_h) + $xmin;
399
400		if ($bit->start != $bit->end) {
401			/* range of bits - draw the bracket */
402			$cx1_l = $x + $bitdim / 4;
403			$cy1 = $y + $bitdim / 4;
404			$im->line($cx1_l, $y, $cx1_l, $cy1);	/* left bar */
405			$cx1_r = $register->bitpos($range[1], $bitdim, $bitset_h) + $xmin;
406			$cx1_r += $bitdim - $bitdim / 4;
407			$im->line($cx1_r, $y, $cx1_r, $cy1);	/* right bar */
408			$im->line($cx1_l, $cy1, $cx1_r, $cy1);	/* lower bar */
409			$cx1 = $cx1_l + ($cx1_r - $cx1_l) / 2;
410		} else {
411			/* just one bit */
412			$cx1 = $x + $xoff;
413			$cy1 = $y + $bitdim / 4;
414			$im->line($cx1, $y, $cx1, $cy1);
415		}
416
417		/* lines from bottom of selection to text */
418		$cy2 = $cy1 + $bitdim * $num_def++ + $yoff;
419		$fw = $desc_adjust * $b_inc;
420		if ($b > $bitset_m) {
421			$cx2 = $xmin + $register->bitpos($b_start, $bitdim, $bitset_h) - $bitdim / 2;
422			$cx2_indent = $desc_adjust - $im->font_width(FONT_LABELS, $text." ");
423		} else {
424			$cx2 = $xmin + $register->bitpos(-1, $bitdim, $bitset_h % $register->bitrange) + $bitdim / 2;
425			$cx2_indent = 0;
426		}
427		$im->line($cx1, $cy1, $cx1, $cy2);	/* vert */
428		$im->line($cx1, $cy2, $cx2 - $cx2_indent, $cy2);	/* horiz */
429
430		/* bit name */
431		$fh = $im->font_height(FONT_LABELS) / 2;
432		$im->text($cx2+$fw, $cy2-$fh, FONT_LABELS, $text);
433		/* bit description */
434		if ($bit->desc != "") {
435			if ($range[1] == $bit->end)
436				$bdesc = $bit->desc;
437			else
438				$bdesc = "See below";
439			$fw += $im->font_width(FONT_DESC, "  ");
440			$text = explode("\n", $bdesc);
441			foreach ($text as $t) {
442				$im->text($cx2+$fw, $cy2+$fh, FONT_DESC, $t);
443				$fh += $im->font_height(FONT_DESC);
444			}
445			$yoff += $fh;
446		}
447
448		$b += ($range[0] - $range[1]) * $b_inc;
449	}
450//echo "$i $yadd " . ($bitdim * ++$num_def + $yoff) ."\n";
451	$yadd = max($yadd, $bitdim * ($num_def+2) + $yoff);
452	}
453//echo "+++ $y $yadd\n";
454	$ymin += $yadd;
455}
456
457	/* cleanup */
458	$ret = $im->output($output_file);
459	$im->destroy();
460	return $ret;
461}
462}
463?>
464