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