diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2014-12-14 11:53:55 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2015-09-11 16:40:19 +0200 |
commit | 1cada1f8339d6b5f8506277f80e62aaef77ab774 (patch) | |
tree | 8477120e97832d659d2ffc471a8bfde73ad4c36e | |
parent | c8d3638b9ebc24e4766714da1c9f961e350799c6 (diff) | |
download | rockbox-1cada1f8339d6b5f8506277f80e62aaef77ab774.tar.gz rockbox-1cada1f8339d6b5f8506277f80e62aaef77ab774.zip |
soc_desc: new version of the desc file format
Fix qeditor to use the old soc_desc_v1.
Port hwstub_shell to the new description format.
Change-Id: I9fefbff534bfaa5c3603bb3dd8307a2b76e88cfc
22 files changed, 3959 insertions, 861 deletions
diff --git a/utils/hwstub/tools/Makefile b/utils/hwstub/tools/Makefile index f718300623..868ddcca79 100644 --- a/utils/hwstub/tools/Makefile +++ b/utils/hwstub/tools/Makefile | |||
@@ -2,9 +2,10 @@ CC=gcc | |||
2 | CXX=g++ | 2 | CXX=g++ |
3 | LD=g++ | 3 | LD=g++ |
4 | HWSTUB_LIB_DIR=../lib | 4 | HWSTUB_LIB_DIR=../lib |
5 | REGTOOLS_INCLUDE_DIR=../../regtools/include | ||
5 | REGTOOLS_LIB_DIR=../../regtools/lib | 6 | REGTOOLS_LIB_DIR=../../regtools/lib |
6 | CFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_LIB_DIR) `pkg-config --cflags lua5.2` | 7 | CFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_INCLUDE_DIR) `pkg-config --cflags lua5.2` |
7 | CXXFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_LIB_DIR) `pkg-config --cflags lua5.2` | 8 | CXXFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_INCLUDE_DIR) `pkg-config --cflags lua5.2` |
8 | LDFLAGS=`pkg-config --libs libusb-1.0` `pkg-config --libs lua5.2` -lreadline -L$(HWSTUB_LIB_DIR) -L$(REGTOOLS_LIB_DIR) -lsocdesc -lhwstub `xml2-config --libs` | 9 | LDFLAGS=`pkg-config --libs libusb-1.0` `pkg-config --libs lua5.2` -lreadline -L$(HWSTUB_LIB_DIR) -L$(REGTOOLS_LIB_DIR) -lsocdesc -lhwstub `xml2-config --libs` |
9 | EXEC=hwstub_shell hwstub_load | 10 | EXEC=hwstub_shell hwstub_load |
10 | SRC=$(wildcard *.c) | 11 | SRC=$(wildcard *.c) |
diff --git a/utils/hwstub/tools/hwstub_shell.cpp b/utils/hwstub/tools/hwstub_shell.cpp index f59ca8b82a..dd5c20b471 100644 --- a/utils/hwstub/tools/hwstub_shell.cpp +++ b/utils/hwstub/tools/hwstub_shell.cpp | |||
@@ -28,11 +28,14 @@ | |||
28 | #include <readline/history.h> | 28 | #include <readline/history.h> |
29 | #include <lua.hpp> | 29 | #include <lua.hpp> |
30 | #include <unistd.h> | 30 | #include <unistd.h> |
31 | #include "soc_desc_v1.hpp" | ||
31 | #include "soc_desc.hpp" | 32 | #include "soc_desc.hpp" |
32 | extern "C" { | 33 | extern "C" { |
33 | #include "prompt.h" | 34 | #include "prompt.h" |
34 | } | 35 | } |
35 | 36 | ||
37 | using namespace soc_desc_v1; | ||
38 | |||
36 | #if LUA_VERSION_NUM < 502 | 39 | #if LUA_VERSION_NUM < 502 |
37 | #warning You need at least lua 5.2 | 40 | #warning You need at least lua 5.2 |
38 | #endif | 41 | #endif |
@@ -51,6 +54,44 @@ struct hwstub_pp_desc_t g_hwdev_pp; | |||
51 | lua_State *g_lua; | 54 | lua_State *g_lua; |
52 | 55 | ||
53 | /** | 56 | /** |
57 | * debug | ||
58 | */ | ||
59 | |||
60 | void print_context(const std::string& file, const soc_desc::error_context_t& ctx) | ||
61 | { | ||
62 | for(size_t j = 0; j < ctx.count(); j++) | ||
63 | { | ||
64 | soc_desc::error_t e = ctx.get(j); | ||
65 | switch(e.level()) | ||
66 | { | ||
67 | case soc_desc::error_t::INFO: printf("[INFO]"); break; | ||
68 | case soc_desc::error_t::WARNING: printf("[WARN]"); break; | ||
69 | case soc_desc::error_t::FATAL: printf("[FATAL]"); break; | ||
70 | default: printf("[UNK]"); break; | ||
71 | } | ||
72 | if(e.location().size() != 0) | ||
73 | printf(" (%s) %s:", file.c_str(), e.location().c_str()); | ||
74 | printf(" %s\n", e.message().c_str()); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | void my_lua_print_stack(lua_State *state = 0, int up_to = 0) | ||
79 | { | ||
80 | if(state == 0) | ||
81 | state = g_lua; | ||
82 | up_to = lua_gettop(state) - up_to; | ||
83 | printf("stack:"); | ||
84 | for(int i = -1; i >= -up_to; i--) | ||
85 | { | ||
86 | if(lua_isstring(state, i)) | ||
87 | printf(" <%s>", lua_tostring(state, i)); | ||
88 | else | ||
89 | printf(" [%s]", lua_typename(state, lua_type(state, i))); | ||
90 | } | ||
91 | printf("\n"); | ||
92 | } | ||
93 | |||
94 | /** | ||
54 | * hw specific | 95 | * hw specific |
55 | */ | 96 | */ |
56 | 97 | ||
@@ -449,7 +490,7 @@ int my_lua_write_field(lua_State *state) | |||
449 | soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); | 490 | soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); |
450 | soc_word_t shift = lua_tounsigned(state, lua_upvalueindex(2)); | 491 | soc_word_t shift = lua_tounsigned(state, lua_upvalueindex(2)); |
451 | soc_word_t mask = lua_tounsigned(state, lua_upvalueindex(3)); | 492 | soc_word_t mask = lua_tounsigned(state, lua_upvalueindex(3)); |
452 | bool is_sct = lua_toboolean(state, lua_upvalueindex(5)); | 493 | char op = lua_tounsigned(state, lua_upvalueindex(5)); |
453 | 494 | ||
454 | soc_word_t value = mask; | 495 | soc_word_t value = mask; |
455 | if(n == 1) | 496 | if(n == 1) |
@@ -469,10 +510,17 @@ int my_lua_write_field(lua_State *state) | |||
469 | value &= mask; | 510 | value &= mask; |
470 | } | 511 | } |
471 | 512 | ||
472 | if(!is_sct) | 513 | soc_word_t old_value = hw_read32(state, addr); |
473 | value = value << shift | (hw_read32(state, addr) & ~(mask << shift)); | 514 | if(op == 'w') |
515 | value = value << shift | (old_value & ~(mask << shift)); | ||
516 | else if(op == 's') | ||
517 | value = old_value | value << shift; | ||
518 | else if(op == 'c') | ||
519 | value = old_value & ~(value << shift); | ||
520 | else if(op == 't') | ||
521 | value = old_value ^ (value << shift); | ||
474 | else | 522 | else |
475 | value <<= shift; | 523 | luaL_error(state, "write_field() internal error"); |
476 | 524 | ||
477 | hw_write32(state, addr, value); | 525 | hw_write32(state, addr, value); |
478 | return 0; | 526 | return 0; |
@@ -501,222 +549,240 @@ int my_lua_sct_reg(lua_State *state) | |||
501 | return 0; | 549 | return 0; |
502 | } | 550 | } |
503 | 551 | ||
504 | void my_lua_create_field(soc_addr_t addr, const soc_reg_field_t& field, bool sct) | 552 | /* lua stack on entry/exit: <reg table> */ |
553 | void my_lua_create_field(soc_addr_t addr, soc_desc::field_ref_t field) | ||
505 | { | 554 | { |
555 | soc_desc::field_t *f = field.get(); | ||
556 | /** create field table */ | ||
506 | lua_newtable(g_lua); | 557 | lua_newtable(g_lua); |
558 | /* lua stack: <field table> <reg table> */ | ||
507 | 559 | ||
508 | lua_pushstring(g_lua, field.name.c_str()); | 560 | /** create various characteristics */ |
561 | lua_pushstring(g_lua, f->name.c_str()); | ||
562 | /* lua stack: <name> <field table> ... */ | ||
509 | lua_setfield(g_lua, -2, "name"); | 563 | lua_setfield(g_lua, -2, "name"); |
564 | /* lua stack: <field table> ... */ | ||
510 | 565 | ||
511 | lua_pushunsigned(g_lua, addr); | 566 | lua_pushunsigned(g_lua, addr); |
567 | /* lua stack: <addr> <field table> ... */ | ||
512 | lua_setfield(g_lua, -2, "addr"); | 568 | lua_setfield(g_lua, -2, "addr"); |
569 | /* lua stack: <field table> ... */ | ||
513 | 570 | ||
514 | lua_pushboolean(g_lua, sct); | 571 | lua_pushunsigned(g_lua, f->pos); |
515 | lua_setfield(g_lua, -2, "sct"); | 572 | /* lua stack: <pos> <field table> ... */ |
516 | 573 | lua_setfield(g_lua, -2, "pos"); | |
517 | lua_pushunsigned(g_lua, field.first_bit); | 574 | /* lua stack: <field table> ... */ |
518 | lua_setfield(g_lua, -2, "first_bit"); | ||
519 | 575 | ||
520 | lua_pushunsigned(g_lua, field.last_bit); | 576 | lua_pushunsigned(g_lua, f->width); |
521 | lua_setfield(g_lua, -2, "last_bit"); | 577 | /* lua stack: <width> <field table> ... */ |
578 | lua_setfield(g_lua, -2, "width"); | ||
579 | /* lua stack: <field table> ... */ | ||
522 | 580 | ||
523 | lua_pushunsigned(g_lua, field.bitmask()); | 581 | lua_pushunsigned(g_lua, f->bitmask()); |
582 | /* lua stack: <bm> <field table> ... */ | ||
524 | lua_setfield(g_lua, -2, "bitmask"); | 583 | lua_setfield(g_lua, -2, "bitmask"); |
584 | /* lua stack: <field table> ... */ | ||
525 | 585 | ||
526 | soc_word_t local_bitmask = field.bitmask() >> field.first_bit; | 586 | soc_word_t local_bitmask = f->bitmask() >> f->pos; |
527 | lua_pushunsigned(g_lua, local_bitmask); | 587 | lua_pushunsigned(g_lua, local_bitmask); |
588 | /* lua stack: <local_bm> <field table> ... */ | ||
528 | lua_setfield(g_lua, -2, "local_bitmask"); | 589 | lua_setfield(g_lua, -2, "local_bitmask"); |
590 | /* lua stack: <field table> ... */ | ||
529 | 591 | ||
592 | /** create read routine */ | ||
530 | lua_pushunsigned(g_lua, addr); | 593 | lua_pushunsigned(g_lua, addr); |
531 | lua_pushunsigned(g_lua, field.first_bit); | 594 | lua_pushunsigned(g_lua, f->pos); |
532 | lua_pushunsigned(g_lua, local_bitmask); | 595 | lua_pushunsigned(g_lua, local_bitmask); |
596 | /* lua stack: <local_bm> <pos> <addr> <field table> ... */ | ||
533 | lua_pushcclosure(g_lua, my_lua_read_field, 3); | 597 | lua_pushcclosure(g_lua, my_lua_read_field, 3); |
598 | /* lua stack: <my_lua_read_field> <field table> ... */ | ||
534 | lua_setfield(g_lua, -2, "read"); | 599 | lua_setfield(g_lua, -2, "read"); |
600 | /* lua stack: <field table> ... */ | ||
535 | 601 | ||
536 | lua_pushunsigned(g_lua, addr); | 602 | /** create write/set/clr/tog routines */ |
537 | lua_pushunsigned(g_lua, field.first_bit); | 603 | static const char *name[] = {"write", "set", "clr", "tog"}; |
538 | lua_pushunsigned(g_lua, local_bitmask); | 604 | static const char arg[] = {'w', 's', 'c', 't'}; |
539 | lua_pushvalue(g_lua, -4); | 605 | for(int i = 0; i < 4; i++) |
540 | lua_pushboolean(g_lua, false); | ||
541 | lua_pushcclosure(g_lua, my_lua_write_field, 5); | ||
542 | lua_setfield(g_lua, -2, "write"); | ||
543 | |||
544 | if(sct) | ||
545 | { | 606 | { |
546 | lua_pushunsigned(g_lua, addr + 4); | 607 | lua_pushunsigned(g_lua, addr); |
547 | lua_pushunsigned(g_lua, field.first_bit); | 608 | lua_pushunsigned(g_lua, f->pos); |
548 | lua_pushunsigned(g_lua, local_bitmask); | 609 | lua_pushunsigned(g_lua, local_bitmask); |
610 | /* lua stack: <local_bm> <pos> <addr> <field table> ... */ | ||
549 | lua_pushvalue(g_lua, -4); | 611 | lua_pushvalue(g_lua, -4); |
550 | lua_pushboolean(g_lua, true); | 612 | /* lua stack: <field table> <local_bm> <pos> <addr> <field table> ... */ |
613 | lua_pushunsigned(g_lua, arg[i]); | ||
614 | /* lua stack: <'wsct'> <field table> <local_bm> <pos> <addr> <field table> ... */ | ||
551 | lua_pushcclosure(g_lua, my_lua_write_field, 5); | 615 | lua_pushcclosure(g_lua, my_lua_write_field, 5); |
552 | lua_setfield(g_lua, -2, "set"); | 616 | /* lua stack: <my_lua_write_field> <field table> ... */ |
553 | 617 | lua_setfield(g_lua, -2, name[i]); | |
554 | lua_pushunsigned(g_lua, addr + 8); | 618 | /* lua stack: <field table> ... */ |
555 | lua_pushunsigned(g_lua, field.first_bit); | ||
556 | lua_pushunsigned(g_lua, local_bitmask); | ||
557 | lua_pushvalue(g_lua, -4); | ||
558 | lua_pushboolean(g_lua, true); | ||
559 | lua_pushcclosure(g_lua, my_lua_write_field, 5); | ||
560 | lua_setfield(g_lua, -2, "clr"); | ||
561 | |||
562 | lua_pushunsigned(g_lua, addr + 12); | ||
563 | lua_pushunsigned(g_lua, field.first_bit); | ||
564 | lua_pushunsigned(g_lua, local_bitmask); | ||
565 | lua_pushvalue(g_lua, -4); | ||
566 | lua_pushboolean(g_lua, true); | ||
567 | lua_pushcclosure(g_lua, my_lua_write_field, 5); | ||
568 | lua_setfield(g_lua, -2, "tog"); | ||
569 | } | 619 | } |
570 | 620 | ||
571 | for(size_t i = 0; i < field.value.size(); i++) | 621 | /** create values */ |
622 | for(size_t i = 0; i < f->enum_.size(); i++) | ||
572 | { | 623 | { |
573 | lua_pushunsigned(g_lua, field.value[i].value); | 624 | lua_pushunsigned(g_lua, f->enum_[i].value); |
574 | lua_setfield(g_lua, -2, field.value[i].name.c_str()); | 625 | /* lua stack: <value> <field table> ... */ |
626 | lua_setfield(g_lua, -2, f->enum_[i].name.c_str()); | ||
627 | /* lua stack: <field table> ... */ | ||
575 | } | 628 | } |
629 | |||
630 | /** register field */ | ||
631 | lua_setfield(g_lua, -2, f->name.c_str()); | ||
632 | /* lua stack: <reg table> */ | ||
576 | } | 633 | } |
577 | 634 | ||
578 | void my_lua_create_reg(soc_addr_t addr, size_t index, const soc_reg_t& reg) | 635 | /* lua stack on entry/exit: <inst table> */ |
636 | void my_lua_create_reg(soc_addr_t addr, soc_desc::register_ref_t reg) | ||
579 | { | 637 | { |
580 | lua_newtable(g_lua); | 638 | if(!reg.valid()) |
581 | 639 | return; | |
582 | lua_pushstring(g_lua, reg.addr[index].name.c_str()); | 640 | /** create read/write routine */ |
583 | lua_setfield(g_lua, -2, "name"); | 641 | lua_pushunsigned(g_lua, addr); |
584 | 642 | /* lua stack: <addr> <inst table> */ | |
585 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr); | ||
586 | lua_setfield(g_lua, -2, "addr"); | ||
587 | |||
588 | lua_pushboolean(g_lua, !!(reg.flags & REG_HAS_SCT)); | ||
589 | lua_setfield(g_lua, -2, "sct"); | ||
590 | |||
591 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr); | ||
592 | lua_pushcclosure(g_lua, my_lua_read_reg, 1); | 643 | lua_pushcclosure(g_lua, my_lua_read_reg, 1); |
644 | /* lua stack: <my_lua_read_reg> <inst table> */ | ||
593 | lua_setfield(g_lua, -2, "read"); | 645 | lua_setfield(g_lua, -2, "read"); |
646 | /* lua stack: <inst table> */ | ||
594 | 647 | ||
595 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr); | 648 | lua_pushunsigned(g_lua, addr); |
649 | /* lua stack: <addr> <inst table> */ | ||
596 | lua_pushcclosure(g_lua, my_lua_write_reg, 1); | 650 | lua_pushcclosure(g_lua, my_lua_write_reg, 1); |
651 | /* lua stack: <my_lua_read_reg> <inst table> */ | ||
597 | lua_setfield(g_lua, -2, "write"); | 652 | lua_setfield(g_lua, -2, "write"); |
653 | /* lua stack: <inst table> */ | ||
598 | 654 | ||
599 | if(reg.flags & REG_HAS_SCT) | 655 | /** create set/clr/tog helpers */ |
656 | static const char *name[] = {"set", "clr", "tog"}; | ||
657 | static const char arg[] = {'s', 'c', 't'}; | ||
658 | for(int i = 0; i < 3; i++) | ||
600 | { | 659 | { |
601 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 4); | 660 | lua_pushunsigned(g_lua, addr); |
602 | lua_pushcclosure(g_lua, my_lua_write_reg, 1); | 661 | /* lua stack: <addr> <inst table> */ |
603 | lua_setfield(g_lua, -2, "set"); | 662 | lua_pushunsigned(g_lua, arg[i]); |
663 | /* lua stack: <'s'/'c'/'t'> <addr> <inst table> */ | ||
664 | lua_pushcclosure(g_lua, my_lua_sct_reg, 2); | ||
665 | /* lua stack: <my_lua_sct_reg> <inst table> */ | ||
666 | lua_setfield(g_lua, -2, name[i]); | ||
667 | /* lua stack: <inst table> */ | ||
668 | } | ||
604 | 669 | ||
605 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 8); | 670 | /** create fields */ |
606 | lua_pushcclosure(g_lua, my_lua_write_reg, 1); | 671 | std::vector< soc_desc::field_ref_t > fields = reg.fields(); |
607 | lua_setfield(g_lua, -2, "clr"); | 672 | for(size_t i = 0; i < fields.size(); i++) |
673 | my_lua_create_field(addr, fields[i]); | ||
674 | } | ||
608 | 675 | ||
609 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 12); | 676 | /* lua stack on entry/exit: <parent table> */ |
610 | lua_pushcclosure(g_lua, my_lua_write_reg, 1); | 677 | void my_lua_create_instances(const std::vector< soc_desc::node_inst_t >& inst) |
611 | lua_setfield(g_lua, -2, "tog"); | 678 | { |
612 | } | 679 | for(size_t i = 0; i < inst.size(); i++) |
613 | else | ||
614 | { | 680 | { |
615 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr); | 681 | /** if the instance is indexed, find the instance table, otherwise create it */ |
616 | lua_pushunsigned(g_lua, 's'); | 682 | if(inst[i].is_indexed()) |
617 | lua_pushcclosure(g_lua, my_lua_sct_reg, 2); | 683 | { |
618 | lua_setfield(g_lua, -2, "set"); | 684 | /** try to get the instance table, otherwise create it */ |
685 | lua_getfield(g_lua, -1, inst[i].name().c_str()); | ||
686 | /* lua stack: <index table> <parent table> */ | ||
687 | if(lua_isnil(g_lua, -1)) | ||
688 | { | ||
689 | lua_pop(g_lua, 1); | ||
690 | lua_newtable(g_lua); | ||
691 | /* lua stack: <index table> <parent table> */ | ||
692 | lua_pushvalue(g_lua, -1); | ||
693 | /* lua stack: <index table> <index table> <parent table> */ | ||
694 | lua_setfield(g_lua, -3, inst[i].name().c_str()); | ||
695 | /* lua stack: <index table> <parent table> */ | ||
696 | } | ||
697 | lua_pushinteger(g_lua, inst[i].index()); | ||
698 | /* lua stack: <index> <index table> <parent table> */ | ||
699 | } | ||
619 | 700 | ||
620 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr); | 701 | /** create a new table for the instance */ |
621 | lua_pushunsigned(g_lua, 'c'); | 702 | lua_newtable(g_lua); |
622 | lua_pushcclosure(g_lua, my_lua_sct_reg, 2); | 703 | /* lua stack: <instance table> [<index> <index table>] <parent table> */ |
623 | lua_setfield(g_lua, -2, "clr"); | ||
624 | 704 | ||
625 | lua_pushunsigned(g_lua, addr + reg.addr[index].addr); | 705 | /** create name and desc fields */ |
626 | lua_pushunsigned(g_lua, 't'); | 706 | lua_pushstring(g_lua, inst[i].node().get()->name.c_str()); |
627 | lua_pushcclosure(g_lua, my_lua_sct_reg, 2); | 707 | /* lua stack: <node name> <instance table> ... */ |
628 | lua_setfield(g_lua, -2, "tog"); | 708 | lua_setfield(g_lua, -2, "name"); |
629 | } | 709 | /* lua stack: <instance table> ... */ |
630 | 710 | ||
631 | for(size_t i = 0; i < reg.field.size(); i++) | 711 | lua_pushstring(g_lua, inst[i].node().get()->desc.c_str()); |
632 | { | 712 | /* lua stack: <node desc> <instance table> ... */ |
633 | my_lua_create_field(addr + reg.addr[index].addr, reg.field[i], | 713 | lua_setfield(g_lua, -2, "desc"); |
634 | reg.flags & REG_HAS_SCT); | 714 | /* lua stack: <instance table> ... */ |
635 | lua_setfield(g_lua, -2, reg.field[i].name.c_str()); | ||
636 | } | ||
637 | } | ||
638 | 715 | ||
639 | void my_lua_create_dev(size_t index, const soc_dev_t& dev) | 716 | lua_pushstring(g_lua, inst[i].node().get()->title.c_str()); |
640 | { | 717 | /* lua stack: <node title> <instance table> ... */ |
641 | lua_newtable(g_lua); | 718 | lua_setfield(g_lua, -2, "title"); |
719 | /* lua stack: <instance table> ... */ | ||
642 | 720 | ||
643 | lua_pushstring(g_lua, dev.addr[index].name.c_str()); | 721 | lua_pushunsigned(g_lua, inst[i].addr()); |
644 | lua_setfield(g_lua, -2, "name"); | 722 | /* lua stack: <node addr> <instance table> ... */ |
723 | lua_setfield(g_lua, -2, "addr"); | ||
724 | /* lua stack: <instance table> ... */ | ||
645 | 725 | ||
646 | lua_pushunsigned(g_lua, dev.addr[index].addr); | 726 | /** create register */ |
647 | lua_setfield(g_lua, -2, "addr"); | 727 | my_lua_create_reg(inst[i].addr(), inst[i].node().reg()); |
648 | 728 | ||
649 | for(size_t i = 0; i < dev.reg.size(); i++) | 729 | /** create subinstances */ |
650 | { | 730 | my_lua_create_instances(inst[i].children()); |
651 | bool table = dev.reg[i].addr.size() > 1; | 731 | /* lua stack: <instance table> [<index> <index table>] <parent table> */ |
652 | if(table) | ||
653 | lua_newtable(g_lua); | ||
654 | else | ||
655 | lua_pushnil(g_lua); | ||
656 | 732 | ||
657 | for(size_t k = 0; k < dev.reg[i].addr.size(); k++) | 733 | if(inst[i].is_indexed()) |
658 | { | 734 | { |
659 | my_lua_create_reg(dev.addr[index].addr, k, dev.reg[i]); | 735 | /* lua stack: <instance table> <index> <index table> <parent table> */ |
660 | if(table) | 736 | lua_settable(g_lua, -3); |
661 | { | 737 | /* lua stack: <index table> <parent table> */ |
662 | lua_pushinteger(g_lua, k); | 738 | lua_pop(g_lua, 1); |
663 | lua_pushvalue(g_lua, -2); | ||
664 | lua_settable(g_lua, -4); | ||
665 | } | ||
666 | lua_setfield(g_lua, -3, dev.reg[i].addr[k].name.c_str()); | ||
667 | } | 739 | } |
668 | |||
669 | if(table) | ||
670 | lua_setfield(g_lua, -2, dev.reg[i].name.c_str()); | ||
671 | else | 740 | else |
672 | lua_pop(g_lua, 1); | 741 | { |
742 | /* lua stack: <instance table> <parent table> */ | ||
743 | lua_setfield(g_lua, -2, inst[i].name().c_str()); | ||
744 | } | ||
745 | /* lua stack: <parent table> */ | ||
673 | } | 746 | } |
674 | } | 747 | } |
675 | 748 | ||
676 | bool my_lua_import_soc(const soc_t& soc) | 749 | bool my_lua_import_soc(soc_desc::soc_t& soc) |
677 | { | 750 | { |
751 | /** remember old stack index to check for unbalanced stack at the end */ | ||
678 | int oldtop = lua_gettop(g_lua); | 752 | int oldtop = lua_gettop(g_lua); |
679 | 753 | ||
754 | /** find hwstub.soc table */ | ||
680 | lua_getglobal(g_lua, "hwstub"); | 755 | lua_getglobal(g_lua, "hwstub"); |
756 | /* lua stack: <hwstub table> */ | ||
681 | lua_getfield(g_lua, -1, "soc"); | 757 | lua_getfield(g_lua, -1, "soc"); |
758 | /* lua stack: <hwstub.soc table> <hwstub table> */ | ||
682 | 759 | ||
760 | /** create a new table for the soc */ | ||
683 | lua_newtable(g_lua); | 761 | lua_newtable(g_lua); |
762 | /* lua stack: <soc table> <hwstub.soc table> <hwstub table> */ | ||
684 | 763 | ||
764 | /** create name and desc fields */ | ||
685 | lua_pushstring(g_lua, soc.name.c_str()); | 765 | lua_pushstring(g_lua, soc.name.c_str()); |
766 | /* lua stack: <soc name> <soc table> <hwstub.soc table> <hwstub table> */ | ||
686 | lua_setfield(g_lua, -2, "name"); | 767 | lua_setfield(g_lua, -2, "name"); |
768 | /* lua stack: <soc table> <hwstub.soc table> <hwstub table> */ | ||
687 | 769 | ||
688 | lua_pushstring(g_lua, soc.desc.c_str()); | 770 | lua_pushstring(g_lua, soc.desc.c_str()); |
771 | /* lua stack: <soc desc> <soc table> <hwstub.soc table> <hwstub table> */ | ||
689 | lua_setfield(g_lua, -2, "desc"); | 772 | lua_setfield(g_lua, -2, "desc"); |
773 | /* lua stack: <soc table> <hwstub.soc table> <hwstub table> */ | ||
690 | 774 | ||
691 | for(size_t i = 0; i < soc.dev.size(); i++) | 775 | /** create instances */ |
692 | { | 776 | soc_desc::soc_ref_t rsoc(&soc); |
693 | bool table = soc.dev[i].addr.size() > 1; | 777 | my_lua_create_instances(rsoc.root_inst().children()); |
694 | if(table) | 778 | /* lua stack: <soc table> <hwstub.soc table> <hwstub table> */ |
695 | lua_newtable(g_lua); | ||
696 | else | ||
697 | lua_pushnil(g_lua); | ||
698 | |||
699 | for(size_t k = 0; k < soc.dev[i].addr.size(); k++) | ||
700 | { | ||
701 | my_lua_create_dev(k, soc.dev[i]); | ||
702 | if(table) | ||
703 | { | ||
704 | lua_pushinteger(g_lua, k + 1); | ||
705 | lua_pushvalue(g_lua, -2); | ||
706 | lua_settable(g_lua, -4); | ||
707 | } | ||
708 | lua_setfield(g_lua, -3, soc.dev[i].addr[k].name.c_str()); | ||
709 | } | ||
710 | |||
711 | if(table) | ||
712 | lua_setfield(g_lua, -2, soc.dev[i].name.c_str()); | ||
713 | else | ||
714 | lua_pop(g_lua, 1); | ||
715 | } | ||
716 | 779 | ||
780 | /** put soc table at hwstub.soc.<soc name> */ | ||
717 | lua_setfield(g_lua, -2, soc.name.c_str()); | 781 | lua_setfield(g_lua, -2, soc.name.c_str()); |
782 | /* lua stack: <hwstub.soc table> <hwstub table> */ | ||
718 | 783 | ||
719 | lua_pop(g_lua, 2); | 784 | lua_pop(g_lua, 2); |
785 | /* lua stack: <> */ | ||
720 | 786 | ||
721 | if(lua_gettop(g_lua) != oldtop) | 787 | if(lua_gettop(g_lua) != oldtop) |
722 | { | 788 | { |
@@ -726,7 +792,7 @@ bool my_lua_import_soc(const soc_t& soc) | |||
726 | return true; | 792 | return true; |
727 | } | 793 | } |
728 | 794 | ||
729 | bool my_lua_import_soc(const std::vector< soc_t >& socs) | 795 | bool my_lua_import_soc(std::vector< soc_desc::soc_t >& socs) |
730 | { | 796 | { |
731 | for(size_t i = 0; i < socs.size(); i++) | 797 | for(size_t i = 0; i < socs.size(); i++) |
732 | { | 798 | { |
@@ -803,15 +869,17 @@ int main(int argc, char **argv) | |||
803 | } | 869 | } |
804 | 870 | ||
805 | // load register descriptions | 871 | // load register descriptions |
806 | std::vector< soc_t > socs; | 872 | std::vector< soc_desc::soc_t > socs; |
807 | for(int i = optind; i < argc; i++) | 873 | for(int i = optind; i < argc; i++) |
808 | { | 874 | { |
809 | socs.push_back(soc_t()); | 875 | socs.push_back(soc_desc::soc_t()); |
810 | if(!soc_desc_parse_xml(argv[i], socs[socs.size() - 1])) | 876 | soc_desc::error_context_t ctx; |
877 | if(!soc_desc::parse_xml(argv[i], socs[socs.size() - 1], ctx)) | ||
811 | { | 878 | { |
812 | printf("Cannot load description '%s'\n", argv[i]); | 879 | printf("Cannot load description file '%s'\n", argv[i]); |
813 | return 2; | 880 | socs.pop_back(); |
814 | } | 881 | } |
882 | print_context(argv[i], ctx); | ||
815 | } | 883 | } |
816 | 884 | ||
817 | // create usb context | 885 | // create usb context |
diff --git a/utils/regtools/Makefile b/utils/regtools/Makefile index 5e3feafd01..fed5d8c8e5 100644 --- a/utils/regtools/Makefile +++ b/utils/regtools/Makefile | |||
@@ -2,8 +2,9 @@ DEFINES= | |||
2 | CC?=gcc | 2 | CC?=gcc |
3 | CXX?=g++ | 3 | CXX?=g++ |
4 | LD?=g++ | 4 | LD?=g++ |
5 | CFLAGS=-g -std=c99 -Wall $(DEFINES) -Ilib | 5 | INCLUDE=-Iinclude/ |
6 | CXXFLAGS=-g -Wall $(DEFINES) -Ilib | 6 | CFLAGS=-g -std=c99 -Wall $(DEFINES) $(INCLUDE) |
7 | CXXFLAGS=-g -Wall $(DEFINES) $(INCLUDE) | ||
7 | LDFLAGS=-Llib -lsocdesc `xml2-config --libs` | 8 | LDFLAGS=-Llib -lsocdesc `xml2-config --libs` |
8 | SRC=$(wildcard *.c) | 9 | SRC=$(wildcard *.c) |
9 | SRCXX=$(wildcard *.cpp) | 10 | SRCXX=$(wildcard *.cpp) |
diff --git a/utils/regtools/desc/regs-example-v1.xml b/utils/regtools/desc/regs-example-v1.xml new file mode 100644 index 0000000000..4f3cf81ff2 --- /dev/null +++ b/utils/regtools/desc/regs-example-v1.xml | |||
@@ -0,0 +1,91 @@ | |||
1 | <?xml version="1.0"?> | ||
2 | <soc name="imx233" desc="i.MX233"> | ||
3 | <dev name="APBH" long_name="APHB DMA" desc="AHB-to-APBH Bridge with DMA" version="3.2.0"> | ||
4 | <addr name="APBH" addr="0x80004000"/> | ||
5 | <reg name="CTRL0" desc="" sct="yes"> | ||
6 | <addr name="CTRL0" addr="0x0"/> | ||
7 | <field name="SFTRST" desc="" bitrange="31:31"/> | ||
8 | <field name="CLKGATE" desc="" bitrange="30:30"/> | ||
9 | <field name="AHB_BURST8_EN" desc="" bitrange="29:29"/> | ||
10 | <field name="APB_BURST4_EN" desc="" bitrange="28:28"/> | ||
11 | <field name="RSVD0" desc="" bitrange="27:24"/> | ||
12 | <field name="RESET_CHANNEL" desc="" bitrange="23:16"> | ||
13 | <value name="SSP1" value="0x2" desc=""/> | ||
14 | <value name="SSP2" value="0x4" desc=""/> | ||
15 | <value name="ATA" value="0x10" desc=""/> | ||
16 | <value name="NAND0" value="0x10" desc=""/> | ||
17 | <value name="NAND1" value="0x20" desc=""/> | ||
18 | <value name="NAND2" value="0x40" desc=""/> | ||
19 | <value name="NAND3" value="0x80" desc=""/> | ||
20 | </field> | ||
21 | <field name="CLKGATE_CHANNEL" desc="" bitrange="15:8"> | ||
22 | <value name="SSP1" value="0x2" desc=""/> | ||
23 | <value name="SSP2" value="0x4" desc=""/> | ||
24 | <value name="ATA" value="0x10" desc=""/> | ||
25 | <value name="NAND0" value="0x10" desc=""/> | ||
26 | <value name="NAND1" value="0x20" desc=""/> | ||
27 | <value name="NAND2" value="0x40" desc=""/> | ||
28 | <value name="NAND3" value="0x80" desc=""/> | ||
29 | </field> | ||
30 | <field name="FREEZE_CHANNEL" desc="" bitrange="7:0"> | ||
31 | <value name="SSP1" value="0x2" desc=""/> | ||
32 | <value name="SSP2" value="0x4" desc=""/> | ||
33 | <value name="ATA" value="0x10" desc=""/> | ||
34 | <value name="NAND0" value="0x10" desc=""/> | ||
35 | <value name="NAND1" value="0x20" desc=""/> | ||
36 | <value name="NAND2" value="0x40" desc=""/> | ||
37 | <value name="NAND3" value="0x80" desc=""/> | ||
38 | </field> | ||
39 | </reg> | ||
40 | <reg name="CTRL1" desc="" sct="yes"> | ||
41 | <addr name="CTRL1" addr="0x10"/> | ||
42 | <field name="RSVD1" desc="" bitrange="31:24"/> | ||
43 | <field name="CH_CMDCMPLT_IRQ_EN" desc="" bitrange="23:16"/> | ||
44 | <field name="RSVD0" desc="" bitrange="15:8"/> | ||
45 | <field name="CH_CMDCMPLT_IRQ" desc="" bitrange="7:0"/> | ||
46 | </reg> | ||
47 | <reg name="CHn_CMD" desc=""> | ||
48 | <formula string="0x60+n*0x70"/> | ||
49 | <addr name="CH0_CMD" addr="0x60"/> | ||
50 | <addr name="CH1_CMD" addr="0xd0"/> | ||
51 | <addr name="CH2_CMD" addr="0x140"/> | ||
52 | <addr name="CH3_CMD" addr="0x1b0"/> | ||
53 | <addr name="CH4_CMD" addr="0x220"/> | ||
54 | <addr name="CH5_CMD" addr="0x290"/> | ||
55 | <addr name="CH6_CMD" addr="0x300"/> | ||
56 | <addr name="CH7_CMD" addr="0x370"/> | ||
57 | <field name="XFER_COUNT" desc="" bitrange="31:16"/> | ||
58 | <field name="CMDWORDS" desc="" bitrange="15:12"/> | ||
59 | <field name="RSVD1" desc="" bitrange="11:9"/> | ||
60 | <field name="HALTONTERMINATE" desc="" bitrange="8:8"/> | ||
61 | <field name="WAIT4ENDCMD" desc="" bitrange="7:7"/> | ||
62 | <field name="SEMAPHORE" desc="" bitrange="6:6"/> | ||
63 | <field name="NANDWAIT4READY" desc="" bitrange="5:5"/> | ||
64 | <field name="NANDLOCK" desc="" bitrange="4:4"/> | ||
65 | <field name="IRQONCMPLT" desc="" bitrange="3:3"/> | ||
66 | <field name="CHAIN" desc="" bitrange="2:2"/> | ||
67 | <field name="COMMAND" desc="" bitrange="1:0"> | ||
68 | <value name="NO_DMA_XFER" value="0x0" desc=""/> | ||
69 | <value name="DMA_WRITE" value="0x1" desc=""/> | ||
70 | <value name="DMA_READ" value="0x2" desc=""/> | ||
71 | <value name="DMA_SENSE" value="0x3" desc=""/> | ||
72 | </field> | ||
73 | </reg> | ||
74 | </dev> | ||
75 | <dev name="SAIF" long_name="Sync Audio Interface" desc="Sync Audio Interface (SAIF)" version="3.2.0"> | ||
76 | <addr name="SAIF1" addr="0x80042000"/> | ||
77 | <addr name="SAIF2" addr="0x80046000"/> | ||
78 | <reg name="DATA" desc="" sct="yes"> | ||
79 | <addr name="DATA" addr="0x20"/> | ||
80 | <field name="PCM_RIGHT" desc="" bitrange="31:16"/> | ||
81 | <field name="PCM_LEFT" desc="" bitrange="15:0"/> | ||
82 | </reg> | ||
83 | <reg name="VERSION" desc=""> | ||
84 | <addr name="VERSION" addr="0x30"/> | ||
85 | <field name="MAJOR" desc="" bitrange="31:24"/> | ||
86 | <field name="MINOR" desc="" bitrange="23:16"/> | ||
87 | <field name="STEP" desc="" bitrange="15:0"/> | ||
88 | </reg> | ||
89 | </dev> | ||
90 | </soc> | ||
91 | |||
diff --git a/utils/regtools/desc/regs-example.xml b/utils/regtools/desc/regs-example.xml new file mode 100644 index 0000000000..6fb8f759fe --- /dev/null +++ b/utils/regtools/desc/regs-example.xml | |||
@@ -0,0 +1,153 @@ | |||
1 | <?xml version="1.0"?> | ||
2 | <soc version="2"> | ||
3 | <name>vsoc</name> | ||
4 | <title>Virtual SOC</title> | ||
5 | <desc>Virtual SoC is a nice and powerful chip.</desc> | ||
6 | <author>Amaury Pouly</author> | ||
7 | <isa>ARM</isa> | ||
8 | <version>0.5</version> | ||
9 | <node> | ||
10 | <name>int</name> | ||
11 | <title>Interrupt Collector</title> | ||
12 | <desc>The interrupt collector controls the routing of interrupt to the processor</desc> | ||
13 | <instance> | ||
14 | <name>ICOLL</name> | ||
15 | <title>Interrupt collector</title> | ||
16 | <address>0x80000000</address> | ||
17 | </instance> | ||
18 | <node> | ||
19 | <name>status</name> | ||
20 | <access>read-only</access> | ||
21 | <title>Interrupt status register</title> | ||
22 | <instance> | ||
23 | <name>STATUS</name> | ||
24 | <address>0x4</address> | ||
25 | </instance> | ||
26 | <register> | ||
27 | <width>8</width> | ||
28 | <field> | ||
29 | <name>VDDIO_BO</name> | ||
30 | <desc>VDDIO brownout interrupt status</desc> | ||
31 | <position>0</position> | ||
32 | </field> | ||
33 | </register> | ||
34 | </node> | ||
35 | <node> | ||
36 | <name>enable</name> | ||
37 | <title>Interrupt enable register</title> | ||
38 | <instance> | ||
39 | <name>ENABLE</name> | ||
40 | <address>0x8</address> | ||
41 | </instance> | ||
42 | <register> | ||
43 | <width>16</width> | ||
44 | <field> | ||
45 | <name>VDDIO_BO</name> | ||
46 | <desc>VDDIO brownout interrupt enable</desc> | ||
47 | <position>0</position> | ||
48 | <width>2</width> | ||
49 | <enum> | ||
50 | <name>DISABLED</name> | ||
51 | <desc>Interrupt is disabled</desc> | ||
52 | <value>0</value> | ||
53 | </enum> | ||
54 | <enum> | ||
55 | <name>ENABLED</name> | ||
56 | <desc>Interrupt is enabled</desc> | ||
57 | <value>1</value> | ||
58 | </enum> | ||
59 | <enum> | ||
60 | <name>NMI</name> | ||
61 | <desc>Interrupt is non-maskable</desc> | ||
62 | <value>2</value> | ||
63 | </enum> | ||
64 | </field> | ||
65 | </register> | ||
66 | <variant> | ||
67 | <type>set</type> | ||
68 | <offset>4</offset> | ||
69 | </variant> | ||
70 | <variant> | ||
71 | <type>clr</type> | ||
72 | <offset>8</offset> | ||
73 | </variant> | ||
74 | </node> | ||
75 | </node> | ||
76 | <node> | ||
77 | <name>gpio</name> | ||
78 | <title>GPIO controller</title> | ||
79 | <desc>A GPIO controller manages several ports</desc> | ||
80 | <instance> | ||
81 | <name>CPU_GPIO</name> | ||
82 | <title>CPU GPIO controller 1 through 3</title> | ||
83 | <range> | ||
84 | <first>1</first> | ||
85 | <count>3</count> | ||
86 | <formula variable="n">0x80001000+(n-1)*0x1000</formula> | ||
87 | </range> | ||
88 | </instance> | ||
89 | <instance> | ||
90 | <name>COP_GPIO</name> | ||
91 | <title>Companion processor GPIO controller</title> | ||
92 | <desc>Although the companion processor GPIO controller is accessible from the CPU, it incurs an extra penalty on the bus</desc> | ||
93 | <address>0x90000000</address> | ||
94 | </instance> | ||
95 | <node> | ||
96 | <name>port</name> | ||
97 | <title>GPIO port</title> | ||
98 | <instance> | ||
99 | <name>PORT</name> | ||
100 | <range> | ||
101 | <first>0</first> | ||
102 | <count>4</count> | ||
103 | <base>0</base> | ||
104 | <stride>0x100</stride> | ||
105 | </range> | ||
106 | </instance> | ||
107 | <node> | ||
108 | <name>input</name> | ||
109 | <title>Input register</title> | ||
110 | <instance> | ||
111 | <name>IN</name> | ||
112 | <address>0</address> | ||
113 | </instance> | ||
114 | <register> | ||
115 | <width>8</width> | ||
116 | <field> | ||
117 | <name>VALUE</name> | ||
118 | <position>0</position> | ||
119 | <width>8</width> | ||
120 | </field> | ||
121 | </register> | ||
122 | </node> | ||
123 | <node> | ||
124 | <name>output_enable</name> | ||
125 | <title>Output enable register</title> | ||
126 | <instance> | ||
127 | <name>OE</name> | ||
128 | <address>0x10</address> | ||
129 | </instance> | ||
130 | <register> | ||
131 | <width>8</width> | ||
132 | <field> | ||
133 | <name>ENABLE</name> | ||
134 | <position>0</position> | ||
135 | <width>8</width> | ||
136 | </field> | ||
137 | </register> | ||
138 | <variant> | ||
139 | <type>set</type> | ||
140 | <address>4</address> | ||
141 | </variant> | ||
142 | <variant> | ||
143 | <type>clr</type> | ||
144 | <address>8</address> | ||
145 | </variant> | ||
146 | <variant> | ||
147 | <type>mask</type> | ||
148 | <address>12</address> | ||
149 | </variant> | ||
150 | </node> | ||
151 | </node> | ||
152 | </node> | ||
153 | </soc> \ No newline at end of file | ||
diff --git a/utils/regtools/desc/XML.txt b/utils/regtools/desc/spec-1.0.txt index 3c28154a1f..3c28154a1f 100644 --- a/utils/regtools/desc/XML.txt +++ b/utils/regtools/desc/spec-1.0.txt | |||
diff --git a/utils/regtools/desc/spec-2.0.txt b/utils/regtools/desc/spec-2.0.txt new file mode 100644 index 0000000000..79b9f6be44 --- /dev/null +++ b/utils/regtools/desc/spec-2.0.txt | |||
@@ -0,0 +1,373 @@ | |||
1 | This file describes the format of the register map based on XML, version 2.0. | ||
2 | |||
3 | 1) Overview | ||
4 | ----------- | ||
5 | |||
6 | 1.1) Nodes and instances | ||
7 | ------------------------ | ||
8 | |||
9 | This specification is based on the concept of "nodes". Nodes are containers | ||
10 | which can contain other nodes and/or a register. Each node can have one or more | ||
11 | addresses (addresses are always relative to the parent node). The idea is that | ||
12 | this hierarchy of nodes generates a number of addresses recursively. The example | ||
13 | below outlines this idea: | ||
14 | |||
15 | <node> | ||
16 | <name>N</name> | ||
17 | <instance> | ||
18 | <name>A</name> | ||
19 | <address>X</address> | ||
20 | </instance> | ||
21 | <instance> | ||
22 | <name>B</name> | ||
23 | <address>Y</address> | ||
24 | </instance> | ||
25 | <!-- HERE --> | ||
26 | </node> | ||
27 | |||
28 | This example creates one node named N and two instances named A and B, | ||
29 | at respective addresses X and Y. This means that all subnodes of this node will | ||
30 | have two copies: one relative to X, which path will be prefixed by "A", and | ||
31 | one relative to Y, which path will be prefixed by "B". | ||
32 | This example below explores this idea in details: | ||
33 | |||
34 | <!-- HERE --> | ||
35 | <node> | ||
36 | <name>S_N</name> | ||
37 | <instance> | ||
38 | <name>C</name> | ||
39 | <address>U</address> | ||
40 | </instance> | ||
41 | <instance> | ||
42 | <name>D</name> | ||
43 | <address>V</address> | ||
44 | </instance> | ||
45 | </node> | ||
46 | |||
47 | In this example, N generates two copies of the sub-node S_N. | ||
48 | The sub-node S_N generates two instances C and D. The whole hierarchy thus generates | ||
49 | four instances: | ||
50 | - A.C at X+U | ||
51 | - A.D at X+V | ||
52 | - B.C at Y+U | ||
53 | - B.D at Y+V | ||
54 | |||
55 | As a note for later, notice that there really are two hierarchies in parallel: | ||
56 | - the node hierarchy: it is composed of N and N.S_N | ||
57 | - the instance hierarchy: it is made up of A, B, A.C, A.D, B.C and B.D | ||
58 | |||
59 | 1.2) Ranges | ||
60 | ----------- | ||
61 | |||
62 | To make things more useful, in particular in case of multiple copies of a register, | ||
63 | we introduce the concept of range addresses with an example: | ||
64 | |||
65 | <node> | ||
66 | <name>N</name> | ||
67 | <instance> | ||
68 | <name>A</name> | ||
69 | <range> | ||
70 | <first>1</first> | ||
71 | <count>5</count> | ||
72 | <base>0x1000</base> | ||
73 | <stride>0x100</stride> | ||
74 | </range> | ||
75 | </instance> | ||
76 | <node> | ||
77 | <name>NN</name> | ||
78 | <instance> | ||
79 | <name>E</name> | ||
80 | <address>0x4</address> | ||
81 | </instance> | ||
82 | </node> | ||
83 | </node> | ||
84 | |||
85 | A range describes a contiguous set of adresses, indexed by a number. One can | ||
86 | specify the first number in the range, and the number of copies. In the case | ||
87 | of a regular pattern (base + n * stride), we can specify a stride | ||
88 | to compute the address of the next copy. In this example, the top-level | ||
89 | nodes generates five copies which path is A[1], A[2], ..., A[5] | ||
90 | and which addresses are 0x1000+1*0x100, 0x1000+2*0x100, ..., 0x1000+5*0x100. | ||
91 | If we add the sub-node to the picture, for each copy we create a instance E | ||
92 | we offset 0x4 from the parent. Overall this generates 5 instances: | ||
93 | - A[1].E at 0x1000+1*0x100+0x4 = 0x1104 | ||
94 | - A[2].E at 0x1000+2*0x100+0x4 = 0x1204 | ||
95 | - A[3].E at 0x1000+3*0x100+0x4 = 0x1304 | ||
96 | - A[4].E at 0x1000+4*0x100+0x4 = 0x1404 | ||
97 | - A[5].E at 0x1000+5*0x100+0x4 = 0x1504 | ||
98 | Note that the intermediate path also define instances, so there are 5 additional | ||
99 | instances in reality: | ||
100 | - A[1] at 0x1100 | ||
101 | - A[2] at 0x1200 | ||
102 | - A[3] at 0x1300 | ||
103 | - A[4] at 0x1400 | ||
104 | - A[5] at 0x1500 | ||
105 | |||
106 | For the record, there is a more general way of specifying a range when it does | ||
107 | not follow a nice regular pattern. One can specify a formula where the parameter | ||
108 | is the index. There are no restrictions on the formula except that it must use | ||
109 | usual arithmetic operators. The example below illustrate such a use: | ||
110 | |||
111 | <node> | ||
112 | <name>N</name> | ||
113 | <instance> | ||
114 | <name>F</name> | ||
115 | <range> | ||
116 | <first>0</first> | ||
117 | <count>4</count> | ||
118 | <formula variable="n">0x50+(n/2)*0x100+(n%2)*0x10</formula> | ||
119 | </range> | ||
120 | </instance> | ||
121 | </node> | ||
122 | |||
123 | In this example we generate four nodes F[0], ..., F[3] with a formula. Here "/" | ||
124 | is the euclidian division and "%" is the modulo operator. Note the use of an | ||
125 | attribute to specify which variable represents the index. The generated addresses | ||
126 | are: | ||
127 | - F[0] at 0x50+(0/2)*0x100+(0%2)*0x10 = 0x50 | ||
128 | - F[1] at 0x50+(1/2)*0x100+(1%2)*0x10 = 0x50+0x10 = 0x60 | ||
129 | - F[2] at 0x50+(2/2)*0x100+(2%2)*0x10 = 0x50+0x100 = 0x150 | ||
130 | - F[3] at 0x50+(3/2)*0x100+(3%2)*0x10 = 0x50+0x100+0x10 = 0x160 | ||
131 | |||
132 | 1.3) Node description | ||
133 | --------------------- | ||
134 | |||
135 | For documentation purposes, node can of course carry some description, as well | ||
136 | as instances. More precisely, nodes can have a title, that is a short description | ||
137 | very much like a chapter title, and a description, this is a free form and potentially | ||
138 | lengthy description of the node. Instances too can have a title and a description. | ||
139 | The following example illustrates this: | ||
140 | |||
141 | <node> | ||
142 | <name>icoll</name> | ||
143 | <title>DMA Controller</title> | ||
144 | <desc>The DMA controller provides uniform DMA facilities to transfer data from | ||
145 | and to peripherals. It uses memory-mapped tables and support chained | ||
146 | transfers.</desc> | ||
147 | <instance> | ||
148 | <name>AHB_DMAC</name> | ||
149 | <address>0x80000000</address> | ||
150 | <title>AHB DMA Controller</title> | ||
151 | <desc>The AHB DMA controller provides DMA facilities for the peripherals | ||
152 | on the AHB bus like the SSP and PIX engines.</desc> | ||
153 | </instance> | ||
154 | <instance> | ||
155 | <name>APB_DMAC</name> | ||
156 | <address>0x8001000</address> | ||
157 | <title>APB DMA Controller</title> | ||
158 | <desc>The APB DMA controller provides DMA facilities for the peripherals | ||
159 | on the APB bus like the I2C and PCM engines.</desc> | ||
160 | </instance> | ||
161 | </node> | ||
162 | |||
163 | 1.4) Register description | ||
164 | -------------------------- | ||
165 | |||
166 | The goal of the register description is of course to describe registers! | ||
167 | To see how registers relate to the node hierarchy, see 1.5, this section focuses | ||
168 | only the description only. | ||
169 | |||
170 | A register carries a lot of information, which is organise logically. A register | ||
171 | can have a width, in bits. By default registers are assumed to be 32-bit wide. | ||
172 | The most useful feature of register description is to describe the fields of | ||
173 | the registers. Each field has a name, a start position and a width. Fields | ||
174 | can also carry a description. Finally, each field can specify enumerated values, | ||
175 | that is named values, for convenience. Enumerated values have a name, a value | ||
176 | and an optional description. The example below illustrates all these concepts: | ||
177 | |||
178 | <register> | ||
179 | <width>8</width> | ||
180 | <field> | ||
181 | <name>MODE</name> | ||
182 | <desc>Interrupt mode</desc> | ||
183 | <position>0</position> | ||
184 | <width>2</width> | ||
185 | <enum> | ||
186 | <name>DISABLED</name> | ||
187 | <desc>Interrupt is disabled</desc> | ||
188 | <value>0</value> | ||
189 | </enum> | ||
190 | <enum> | ||
191 | <name>ENABLED</name> | ||
192 | <desc>Interrupt is enabled</desc> | ||
193 | <value>1</value> | ||
194 | </enum> | ||
195 | <enum> | ||
196 | <name>NMI</name> | ||
197 | <desc>Interrupt is non-maskable</desc> | ||
198 | <value>2</value> | ||
199 | </enum> | ||
200 | </field> | ||
201 | <field> | ||
202 | <name>PRIORITY</name> | ||
203 | <desc>Interrupt priority, lower values are more prioritized.</desc> | ||
204 | <position>2</position> | ||
205 | <width>2</width> | ||
206 | </field> | ||
207 | <field> | ||
208 | <name>ARM_MODE</name> | ||
209 | <desc>Select between ARM's FIQ and IRQ mode</desc> | ||
210 | <position>4</position> | ||
211 | <width>1</width> | ||
212 | <enum> | ||
213 | <name>IRQ</name> | ||
214 | <value>0</value> | ||
215 | </enum> | ||
216 | <enum> | ||
217 | <name>FIQ</name> | ||
218 | <value>1</value> | ||
219 | </enum> | ||
220 | </field> | ||
221 | </register> | ||
222 | |||
223 | In this example, the 8-bit registers has three fields: | ||
224 | - MODE(1:0): it has three named values DISABLED(0), ENABLED(1) and NMI(2) | ||
225 | - PRIORITY(2:1): it has no named values | ||
226 | - ARM_MODE(3): it has two named values IRQ(0) and FIQ(1) | ||
227 | |||
228 | 1.5) Register inheritance | ||
229 | ------------------------- | ||
230 | |||
231 | The node hierarchy specifies instances, that is pairs of (path,address), | ||
232 | and the register description describes the internal of a register. The placement | ||
233 | of the register descriptions in the node hierarchy will specify which registers | ||
234 | can be found at each address. More precisely, if a node contains a register | ||
235 | description, it means that this node's and all sub-nodes' instances are registers | ||
236 | following the description. It is forbidden for a node to contain a register | ||
237 | description if one of its parents already contains one. The example below | ||
238 | will make this concept clearer (for readability, we omitted some of the tags): | ||
239 | |||
240 | <node> | ||
241 | <name>dma</name> | ||
242 | <instance><name>DMAC</name><address>0x80000000</address></instance> | ||
243 | <node> | ||
244 | <instance><name>PCM_CHAN</name><address>0x0</address></instance> | ||
245 | <instance><name>I2C_CHAN</name><address>0x10</address></instance> | ||
246 | <register><!--- blabla --></register> | ||
247 | <node> | ||
248 | <name>sct</name> | ||
249 | <instance><name>SET</name><address>0x4</address></instance> | ||
250 | <instance><name>CLR</name><address>0x8</address></instance> | ||
251 | <instance><name>TOG</name><address>0xC</address></instance> | ||
252 | </node> | ||
253 | </node> | ||
254 | </node> | ||
255 | |||
256 | This example describes one register (let's call it blabla) and 9 instances: | ||
257 | - DMAC at 0x80000000, no register | ||
258 | - DMAC.PCM_CHAN at 0x80000000, register blabla | ||
259 | - DMAC.PCM_CHAN.SET at 0x80000004, register blabla | ||
260 | - DMAC.PCM_CHAN.CLR at 0x80000008, register blabla | ||
261 | - DMAC.PCM_CHAN.TOG at 0x8000000C, register blabla | ||
262 | - DMAC.I2C_CHAN at 0x80000010, register blabla | ||
263 | - DMAC.I2C_CHAN.SET at 0x80000014, register blabla | ||
264 | - DMAC.I2C_CHAN.CLR at 0x80000018, register blabla | ||
265 | - DMAC.I2C_CHAN.TOG at 0x8000001C, register blabla | ||
266 | |||
267 | 1.6) Soc description | ||
268 | -------------------- | ||
269 | |||
270 | The description file must also specify some information about the system-on-chip | ||
271 | itself. The entire description, including nodes, is contained in a "soc" tag | ||
272 | which must at least specify the soc name. It can optionally specify the title | ||
273 | and description, as well as the author(s) of the description, the ISA and | ||
274 | the version. | ||
275 | |||
276 | <soc> | ||
277 | <name>vsoc</name> | ||
278 | <title>Virtual SOC</title> | ||
279 | <desc>Virtual SoC is a nice and powerful chip.</desc> | ||
280 | <author>Amaury Pouly</author> | ||
281 | <isa>ARM</isa> | ||
282 | <version>0.5</version> | ||
283 | <!-- put nodes below --> | ||
284 | </soc> | ||
285 | |||
286 | 2) Specification | ||
287 | ---------------- | ||
288 | |||
289 | Root | ||
290 | ---- | ||
291 | As any XML document, the content of the file should be enclosed in a "xml" tag. | ||
292 | The root element must be "soc" tag. | ||
293 | |||
294 | Example: | ||
295 | <?xml version="1.0"?> | ||
296 | <!-- desc --> | ||
297 | </xml> | ||
298 | |||
299 | Element: soc | ||
300 | ------------ | ||
301 | It must contain the following tags: | ||
302 | - name: name of soc, only made of alphanumerical characters | ||
303 | It can contain at most one of each of the following tags: | ||
304 | - title: one line description of the soc | ||
305 | - desc: free form description of the soc | ||
306 | - isa: instruction set assembly | ||
307 | - version: version of the description | ||
308 | It can contain any number of the following tags: | ||
309 | - author: author of the description | ||
310 | - node: node description | ||
311 | |||
312 | Element: node | ||
313 | ------------- | ||
314 | It must contain the following tags: | ||
315 | - name: name of node, only made of alphanumerical characters | ||
316 | It can contain at most one of each of the following tags: | ||
317 | - title: one line description of the node | ||
318 | - desc: free form description of the node | ||
319 | - register: register description | ||
320 | It can contain any number of the following tags: | ||
321 | - instance: author of the description | ||
322 | - node: node description | ||
323 | |||
324 | Element: instance | ||
325 | ----------------- | ||
326 | It must contain the following tags: | ||
327 | - name: name of instance, only made of alphanumerical characters | ||
328 | It can contain at most one of each of the following tags: | ||
329 | - title: one line description of the instance | ||
330 | - desc: free form description of the instance | ||
331 | - address: address for a single instance (non-negative number) | ||
332 | - range: address range for multiple instances | ||
333 | Note that address and range are mutually exclusive, and at least one of them | ||
334 | must exists. | ||
335 | |||
336 | Element: range | ||
337 | -------------- | ||
338 | It must contain the following tags: | ||
339 | - first: index of the first instance (non-negative number) | ||
340 | - count: number of instances (positive number) | ||
341 | It can contain at most one of each of the following tags: | ||
342 | - base: base address (non-negative number) | ||
343 | - stride: stride (number) | ||
344 | - formula: free-form formula, must have a "variable" attribute | ||
345 | Note that stride and formula are mutually exclusive, and at least one of them | ||
346 | must exists. If stride is specified and base is omitted, it is taken to be 0. | ||
347 | |||
348 | Element: register | ||
349 | ----------------- | ||
350 | It can contain at most one of each of the following tags: | ||
351 | - width: width in bits (positive number) | ||
352 | It can contain any number of the following tags: | ||
353 | - field: field description | ||
354 | |||
355 | Element: field | ||
356 | -------------- | ||
357 | It must contain the following tags: | ||
358 | - name: name of field, only made of alphanumerical characters | ||
359 | - position: least significant bit | ||
360 | It can contain at most one of each of the following tags: | ||
361 | - desc: free form description of the instance | ||
362 | - width: width in bits | ||
363 | It can contain any number of the following tags: | ||
364 | - enum: enumerated value | ||
365 | If the width is not specified, it is assumed to be 1. | ||
366 | |||
367 | Element: enum | ||
368 | ------------- | ||
369 | It must contain the following tags: | ||
370 | - name: name of field, only made of alphanumerical characters | ||
371 | - value: value (non-negative, must fit into the field's width) | ||
372 | It can contain at most one of each of the following tags: | ||
373 | - desc: free form description of the instance \ No newline at end of file | ||
diff --git a/utils/regtools/headergen.cpp b/utils/regtools/headergen_v1.cpp index 046e8b2fd8..7b38366d5d 100644 --- a/utils/regtools/headergen.cpp +++ b/utils/regtools/headergen_v1.cpp | |||
@@ -18,7 +18,7 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "soc_desc.hpp" | 21 | #include "soc_desc_v1.hpp" |
22 | #include <stdio.h> | 22 | #include <stdio.h> |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <algorithm> | 24 | #include <algorithm> |
@@ -28,6 +28,8 @@ | |||
28 | #include <sys/types.h> | 28 | #include <sys/types.h> |
29 | #include <getopt.h> | 29 | #include <getopt.h> |
30 | 30 | ||
31 | using namespace soc_desc_v1; | ||
32 | |||
31 | #define HEADERGEN_VERSION "2.1.8" | 33 | #define HEADERGEN_VERSION "2.1.8" |
32 | 34 | ||
33 | #define error(...) do{ fprintf(stderr, __VA_ARGS__); exit(1); } while(0) | 35 | #define error(...) do{ fprintf(stderr, __VA_ARGS__); exit(1); } while(0) |
@@ -721,7 +723,7 @@ int main(int argc, char **argv) | |||
721 | for(int i = optind; i < argc - 1; i++) | 723 | for(int i = optind; i < argc - 1; i++) |
722 | { | 724 | { |
723 | soc_t s; | 725 | soc_t s; |
724 | if(!soc_desc_parse_xml(argv[i], s)) | 726 | if(!parse_xml(argv[i], s)) |
725 | { | 727 | { |
726 | printf("Cannot parse %s\n", argv[i]); | 728 | printf("Cannot parse %s\n", argv[i]); |
727 | return 1; | 729 | return 1; |
diff --git a/utils/regtools/include/soc_desc.hpp b/utils/regtools/include/soc_desc.hpp new file mode 100644 index 0000000000..66f2e3b6e1 --- /dev/null +++ b/utils/regtools/include/soc_desc.hpp | |||
@@ -0,0 +1,384 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef __SOC_DESC__ | ||
22 | #define __SOC_DESC__ | ||
23 | |||
24 | #include <stdint.h> | ||
25 | #include <vector> | ||
26 | #include <list> | ||
27 | #include <string> | ||
28 | #include <map> | ||
29 | |||
30 | namespace soc_desc | ||
31 | { | ||
32 | |||
33 | const size_t MAJOR_VERSION = 2; | ||
34 | const size_t MINOR_VERSION = 0; | ||
35 | const size_t REVISION_VERSION = 0; | ||
36 | |||
37 | /** Typedef for SoC types: word, address and flags */ | ||
38 | typedef uint32_t soc_addr_t; | ||
39 | typedef uint32_t soc_word_t; | ||
40 | typedef int soc_id_t; | ||
41 | |||
42 | /** Error class */ | ||
43 | class error_t | ||
44 | { | ||
45 | public: | ||
46 | enum level_t | ||
47 | { | ||
48 | INFO, | ||
49 | WARNING, | ||
50 | FATAL | ||
51 | }; | ||
52 | error_t(level_t lvl, const std::string& loc, const std::string& msg) | ||
53 | :m_level(lvl), m_loc(loc), m_msg(msg) {} | ||
54 | level_t level() const { return m_level; } | ||
55 | std::string location() const { return m_loc; } | ||
56 | std::string message() const { return m_msg; } | ||
57 | protected: | ||
58 | level_t m_level; | ||
59 | std::string m_loc, m_msg; | ||
60 | }; | ||
61 | |||
62 | /** Error context to log errors */ | ||
63 | class error_context_t | ||
64 | { | ||
65 | public: | ||
66 | void add(const error_t& err) { m_list.push_back(err); } | ||
67 | size_t count() const { return m_list.size(); } | ||
68 | error_t get(size_t i) const { return m_list[i]; } | ||
69 | protected: | ||
70 | std::vector< error_t > m_list; | ||
71 | }; | ||
72 | |||
73 | /** | ||
74 | * Bare representation of the format | ||
75 | */ | ||
76 | |||
77 | /** Enumerated value (aka named value), represents a special value for a field */ | ||
78 | struct enum_t | ||
79 | { | ||
80 | soc_id_t id; /** ID (must be unique among field enums) */ | ||
81 | std::string name; /** Name (must be unique among field enums) */ | ||
82 | std::string desc; /** Optional description of the meaning of this value */ | ||
83 | soc_word_t value; /** Value of the field */ | ||
84 | }; | ||
85 | |||
86 | /** Register field information */ | ||
87 | struct field_t | ||
88 | { | ||
89 | soc_id_t id; /** ID (must be unique among register fields) */ | ||
90 | std::string name; /** Name (must be unique among register fields) */ | ||
91 | std::string desc; /** Optional description of the field */ | ||
92 | size_t pos; /** Position of the least significant bit */ | ||
93 | size_t width; /** Width of the field in bits */ | ||
94 | std::vector< enum_t > enum_; /** List of special values */ | ||
95 | |||
96 | /** Returns the bit mask of the field within the register */ | ||
97 | soc_word_t bitmask() const | ||
98 | { | ||
99 | // WARNING beware of the case where width is 32 | ||
100 | if(width == 32) | ||
101 | return 0xffffffff; | ||
102 | else | ||
103 | return ((1 << width) - 1) << pos; | ||
104 | } | ||
105 | |||
106 | /** Extract field value from register value */ | ||
107 | soc_word_t extract(soc_word_t reg_val) const | ||
108 | { | ||
109 | return (reg_val & bitmask()) >> pos; | ||
110 | } | ||
111 | |||
112 | /** Replace the field value in a register value */ | ||
113 | soc_word_t replace(soc_word_t reg_val, soc_word_t field_val) const | ||
114 | { | ||
115 | return (reg_val & ~bitmask()) | ((field_val << pos) & bitmask()); | ||
116 | } | ||
117 | |||
118 | /** Return field value index, or -1 if none */ | ||
119 | int find_value(soc_word_t v) const | ||
120 | { | ||
121 | for(size_t i = 0; i < enum_.size(); i++) | ||
122 | if(enum_[i].value == v) | ||
123 | return i; | ||
124 | return -1; | ||
125 | } | ||
126 | }; | ||
127 | |||
128 | /** Register information */ | ||
129 | struct register_t | ||
130 | { | ||
131 | size_t width; /** Size in bits */ | ||
132 | std::vector< field_t > field; /** List of fields */ | ||
133 | }; | ||
134 | |||
135 | /** Node address range information */ | ||
136 | struct range_t | ||
137 | { | ||
138 | enum type_t | ||
139 | { | ||
140 | STRIDE, /** Addresses are given by a base address and a stride */ | ||
141 | FORMULA /** Addresses are given by a formula */ | ||
142 | }; | ||
143 | |||
144 | type_t type; /** Range type */ | ||
145 | size_t first; /** First index in the range */ | ||
146 | size_t count; /** Number of indexes in the range */ | ||
147 | soc_word_t base; /** Base address (for STRIDE) */ | ||
148 | soc_word_t stride; /** Stride value (for STRIDE) */ | ||
149 | std::string formula; /** Formula (for FORMULA) */ | ||
150 | std::string variable; /** Formula variable name (for FORMULA) */ | ||
151 | }; | ||
152 | |||
153 | /** Node instance information */ | ||
154 | struct instance_t | ||
155 | { | ||
156 | enum type_t | ||
157 | { | ||
158 | SINGLE, /** There is a single instance at a specified address */ | ||
159 | RANGE /** There are multiple addresses forming a range */ | ||
160 | }; | ||
161 | |||
162 | soc_id_t id; /** ID (must be unique among node instances) */ | ||
163 | std::string name; /** Name (must be unique among node instances) */ | ||
164 | std::string title; /** Optional instance human name */ | ||
165 | std::string desc; /** Optional description of the instance */ | ||
166 | type_t type; /** Instance type */ | ||
167 | soc_word_t addr; /** Address (for SINGLE) */ | ||
168 | range_t range; /** Range (for RANGE) */ | ||
169 | }; | ||
170 | |||
171 | /** Node information */ | ||
172 | struct node_t | ||
173 | { | ||
174 | soc_id_t id; /** ID (must be unique among nodes) */ | ||
175 | std::string name; /** Name (must be unique for the among nodes) */ | ||
176 | std::string title; /** Optional node human name */ | ||
177 | std::string desc; /** Optional description of the node */ | ||
178 | std::vector< register_t> register_; /** Optional register */ | ||
179 | std::vector< instance_t> instance; /** List of instances */ | ||
180 | std::vector< node_t > node; /** List of sub-nodes */ | ||
181 | }; | ||
182 | |||
183 | /** System-on-chip information */ | ||
184 | struct soc_t | ||
185 | { | ||
186 | std::string name; /** Codename of the SoC */ | ||
187 | std::string title; /** Human name of the SoC */ | ||
188 | std::string desc; /** Optional description of the SoC */ | ||
189 | std::string isa; /** Instruction Set Assembly */ | ||
190 | std::string version; /** Description version */ | ||
191 | std::vector< std::string > author; /** List of authors of the description */ | ||
192 | std::vector< node_t > node; /** List of nodes */ | ||
193 | }; | ||
194 | |||
195 | /** Parse a SoC description from a XML file, put it into <soc>. */ | ||
196 | bool parse_xml(const std::string& filename, soc_t& soc, error_context_t& error_ctx); | ||
197 | /** Write a SoC description to a XML file, overwriting it. A file can contain | ||
198 | * multiple Soc descriptions */ | ||
199 | bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& error_ctx); | ||
200 | /** Formula parser: try to parse and evaluate a formula with some variables */ | ||
201 | bool evaluate_formula(const std::string& formula, | ||
202 | const std::map< std::string, soc_word_t>& var, soc_word_t& result, | ||
203 | const std::string& loc, error_context_t& error_ctx); | ||
204 | |||
205 | /** | ||
206 | * Convenience API to manipulate the format | ||
207 | * | ||
208 | * The idea is that *_ref_t objects are stable pointers: they stay valid even | ||
209 | * when the underlying soc changes. In particular: | ||
210 | * - modifying any structure data (except id fields) preserves all references | ||
211 | * - removing a structure invalidates all references pointing to this structure | ||
212 | * and its children | ||
213 | * - adding any structure preserves all references | ||
214 | * These references can be used to get pointers to the actual data | ||
215 | * of the representation when it needs to be read or write. | ||
216 | */ | ||
217 | |||
218 | class soc_ref_t; | ||
219 | class node_ref_t; | ||
220 | class register_ref_t; | ||
221 | class field_ref_t; | ||
222 | class node_inst_t; | ||
223 | |||
224 | /** SoC reference */ | ||
225 | class soc_ref_t | ||
226 | { | ||
227 | soc_t *m_soc; /* pointer to the soc */ | ||
228 | public: | ||
229 | /** Builds an invalid reference */ | ||
230 | soc_ref_t(); | ||
231 | /** Builds a reference to a soc */ | ||
232 | soc_ref_t(soc_t *soc); | ||
233 | /** Checks whether this reference is valid */ | ||
234 | bool valid() const; | ||
235 | /** Returns a pointer to the soc */ | ||
236 | soc_t *get() const; | ||
237 | /** Returns a reference to the root node */ | ||
238 | node_ref_t root() const; | ||
239 | /** Returns a reference to the root node instance */ | ||
240 | node_inst_t root_inst() const; | ||
241 | /** Compare this reference to another */ | ||
242 | bool operator==(const soc_ref_t& r) const; | ||
243 | inline bool operator!=(const soc_ref_t& r) const { return !operator==(r); } | ||
244 | }; | ||
245 | |||
246 | /** SoC node reference | ||
247 | * NOTE: the root soc node is presented as a node with empty path */ | ||
248 | class node_ref_t | ||
249 | { | ||
250 | friend class soc_ref_t; | ||
251 | friend class node_inst_t; | ||
252 | soc_ref_t m_soc; /* reference to the soc */ | ||
253 | std::vector< soc_id_t > m_path; /* path from the root */ | ||
254 | |||
255 | node_ref_t(soc_ref_t soc); | ||
256 | node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path); | ||
257 | public: | ||
258 | /** Builds an invalid reference */ | ||
259 | node_ref_t(); | ||
260 | /** Check whether this reference is valid */ | ||
261 | bool valid() const; | ||
262 | /** Check whether this reference is the root node */ | ||
263 | bool is_root() const; | ||
264 | /** Returns a pointer to the node, or 0 if invalid or root */ | ||
265 | node_t *get() const; | ||
266 | /** Returns a reference to the soc */ | ||
267 | soc_ref_t soc() const; | ||
268 | /** Returns a reference to the parent node */ | ||
269 | node_ref_t parent() const; | ||
270 | /** Returns a reference to the register (which may be on a parent node) */ | ||
271 | register_ref_t reg() const; | ||
272 | /** Returns a list of references to the sub-nodes */ | ||
273 | std::vector< node_ref_t > children() const; | ||
274 | /** Returns a reference to a specific child */ | ||
275 | node_ref_t child(const std::string& name) const; | ||
276 | /** Returns the path of the node, as the list of node names from the root */ | ||
277 | std::vector< std::string > path() const; | ||
278 | /** Returns the name of the node */ | ||
279 | std::string name() const; | ||
280 | /** Compare this reference to another */ | ||
281 | bool operator==(const node_ref_t& r) const; | ||
282 | inline bool operator!=(const node_ref_t& r) const { return !operator==(r); } | ||
283 | }; | ||
284 | |||
285 | /** SoC register reference */ | ||
286 | class register_ref_t | ||
287 | { | ||
288 | friend class node_ref_t; | ||
289 | node_ref_t m_node; /* reference to the node owning the register */ | ||
290 | |||
291 | register_ref_t(node_ref_t node); | ||
292 | public: | ||
293 | /** Builds an invalid reference */ | ||
294 | register_ref_t(); | ||
295 | /** Check whether this reference is valid/exists */ | ||
296 | bool valid() const; | ||
297 | /** Returns a pointer to the register, or 0 */ | ||
298 | register_t *get() const; | ||
299 | /** Returns a reference to the node containing the register */ | ||
300 | node_ref_t node() const; | ||
301 | /** Returns a list of references to the fields of the register */ | ||
302 | std::vector< field_ref_t > fields() const; | ||
303 | /** Returns a reference to a particular field */ | ||
304 | field_ref_t field(const std::string& name) const; | ||
305 | /** Compare this reference to another */ | ||
306 | bool operator==(const register_ref_t& r) const; | ||
307 | inline bool operator!=(const register_ref_t& r) const { return !operator==(r); } | ||
308 | }; | ||
309 | |||
310 | /** SoC register field reference */ | ||
311 | class field_ref_t | ||
312 | { | ||
313 | friend class register_ref_t; | ||
314 | register_ref_t m_reg; /* reference to the register */ | ||
315 | soc_id_t m_id; /* field name */ | ||
316 | |||
317 | field_ref_t(register_ref_t reg, soc_id_t id); | ||
318 | public: | ||
319 | /** Builds an invalid reference */ | ||
320 | field_ref_t(); | ||
321 | /** Check whether this reference is valid/exists */ | ||
322 | bool valid() const; | ||
323 | /** Returns a pointer to the field, or 0 */ | ||
324 | field_t *get() const; | ||
325 | /** Returns a reference to the register containing the field */ | ||
326 | register_ref_t reg() const; | ||
327 | /** Compare this reference to another */ | ||
328 | bool operator==(const field_ref_t& r) const; | ||
329 | inline bool operator!=(const field_ref_t& r) const { return !operator==(r); } | ||
330 | }; | ||
331 | |||
332 | /** SoC node instance | ||
333 | * NOTE: the root soc node is presented as a node with a single instance at 0 */ | ||
334 | class node_inst_t | ||
335 | { | ||
336 | friend class node_ref_t; | ||
337 | friend class soc_ref_t; | ||
338 | node_ref_t m_node; /* reference to the node */ | ||
339 | std::vector< soc_id_t > m_id_path; /* list of instance IDs */ | ||
340 | std::vector< size_t > m_index_path; /* list of instance indexes */ | ||
341 | |||
342 | node_inst_t(soc_ref_t soc); | ||
343 | node_inst_t(node_ref_t soc, const std::vector< soc_id_t >& path, | ||
344 | const std::vector< size_t >& indexes); | ||
345 | public: | ||
346 | /** Builds an invalid reference */ | ||
347 | node_inst_t(); | ||
348 | /** Check whether this instance is valid/exists */ | ||
349 | bool valid() const; | ||
350 | /** Returns a reference to the soc */ | ||
351 | soc_ref_t soc() const; | ||
352 | /** Returns a reference to the node */ | ||
353 | node_ref_t node() const; | ||
354 | /** Check whether this reference is the root node instance */ | ||
355 | bool is_root() const; | ||
356 | /** Returns a reference to the parent instance */ | ||
357 | node_inst_t parent() const; | ||
358 | /** Returns a pointer to the instance of the node, or 0 */ | ||
359 | instance_t *get() const; | ||
360 | /** Returns the address of this instance */ | ||
361 | soc_addr_t addr() const; | ||
362 | /** Returns an instance to a child of this node's instance. If the subnode | ||
363 | * instance is a range, the returned reference is invalid */ | ||
364 | node_inst_t child(const std::string& name) const; | ||
365 | /** Returns an instance to a child of this node's instance with a range index. | ||
366 | * If the subnode is not not a range or if the index is out of bounds, | ||
367 | * the returned reference is invalid */ | ||
368 | node_inst_t child(const std::string& name, size_t index) const; | ||
369 | /** Returns a list of all instances of subnodes of this node's instance */ | ||
370 | std::vector< node_inst_t > children() const; | ||
371 | /** Returns the name of the instance */ | ||
372 | std::string name() const; | ||
373 | /** Checks whether this instance is indexed */ | ||
374 | bool is_indexed() const; | ||
375 | /** Returns the index of the instance */ | ||
376 | size_t index() const; | ||
377 | /** Compare this reference to another */ | ||
378 | bool operator==(const node_inst_t& r) const; | ||
379 | inline bool operator!=(const node_inst_t& r) const { return !operator==(r); } | ||
380 | }; | ||
381 | |||
382 | } // soc_desc | ||
383 | |||
384 | #endif /* __SOC_DESC__ */ | ||
diff --git a/utils/regtools/lib/soc_desc.hpp b/utils/regtools/include/soc_desc_v1.hpp index bb8eb4ba8d..33368e88d4 100644 --- a/utils/regtools/lib/soc_desc.hpp +++ b/utils/regtools/include/soc_desc_v1.hpp | |||
@@ -18,8 +18,8 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #ifndef __SOC_DESC__ | 21 | #ifndef __SOC_DESC_V1__ |
22 | #define __SOC_DESC__ | 22 | #define __SOC_DESC_V1__ |
23 | 23 | ||
24 | #include <stdint.h> | 24 | #include <stdint.h> |
25 | #include <vector> | 25 | #include <vector> |
@@ -45,13 +45,12 @@ | |||
45 | * ignores the position of the WORD_LENGTH field in the register. | 45 | * ignores the position of the WORD_LENGTH field in the register. |
46 | */ | 46 | */ |
47 | 47 | ||
48 | #define SOCDESC_VERSION_MAJOR 1 | 48 | namespace soc_desc_v1 |
49 | #define SOCDESC_VERSION_MINOR 4 | 49 | { |
50 | #define SOCDESC_VERSION_REV 1 | ||
51 | 50 | ||
52 | #define SOCDESC_VERSION__(maj,min,rev) #maj"."#min"."#rev | 51 | const size_t MAJOR_VERSION = 1; |
53 | #define SOCDESC_VERSION_(maj,min,rev) SOCDESC_VERSION__(maj,min,rev) | 52 | const size_t MINOR_VERSION = 4; |
54 | #define SOCDESC_VERSION SOCDESC_VERSION_(SOCDESC_VERSION_MAJOR,SOCDESC_VERSION_MINOR,SOCDESC_VERSION_REV) | 53 | const size_t REVISION_VERSION = 1; |
55 | 54 | ||
56 | /** | 55 | /** |
57 | * Typedef for SoC types: word, address and flags */ | 56 | * Typedef for SoC types: word, address and flags */ |
@@ -211,18 +210,21 @@ struct soc_t | |||
211 | }; | 210 | }; |
212 | 211 | ||
213 | /** Parse a SoC description from a XML file, append it to <soc>. */ | 212 | /** Parse a SoC description from a XML file, append it to <soc>. */ |
214 | bool soc_desc_parse_xml(const std::string& filename, soc_t& soc); | 213 | bool parse_xml(const std::string& filename, soc_t& soc); |
215 | /** Write a SoC description to a XML file, overwriting it. A file can contain | 214 | /** Write a SoC description to a XML file, overwriting it. A file can contain |
216 | * multiple Soc descriptions */ | 215 | * multiple Soc descriptions */ |
217 | bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc); | 216 | bool produce_xml(const std::string& filename, const soc_t& soc); |
218 | /** Normalise a soc description by reordering elemnts so that: | 217 | /** Normalise a soc description by reordering elemnts so that: |
219 | * - devices are sorted by first name | 218 | * - devices are sorted by first name |
220 | * - registers are sorted by first address | 219 | * - registers are sorted by first address |
221 | * - fields are sorted by last bit | 220 | * - fields are sorted by last bit |
222 | * - values are sorted by value */ | 221 | * - values are sorted by value */ |
223 | void soc_desc_normalize(soc_t& soc); | 222 | void normalize(soc_t& soc); |
224 | /** Formula parser: try to parse and evaluate a formula to a specific value of 'n' */ | 223 | /** Formula parser: try to parse and evaluate a formula with some variables */ |
225 | bool soc_desc_evaluate_formula(const std::string& formula, | 224 | bool evaluate_formula(const std::string& formula, |
226 | const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error); | 225 | const std::map< std::string, soc_word_t>& var, soc_word_t& result, |
226 | std::string& error); | ||
227 | |||
228 | } // soc_desc_v1 | ||
227 | 229 | ||
228 | #endif /* __SOC_DESC__ */ \ No newline at end of file | 230 | #endif /* __SOC_DESC_V1__ */ \ No newline at end of file |
diff --git a/utils/regtools/lib/Makefile b/utils/regtools/lib/Makefile index 08021ef941..d7d6c1b07a 100644 --- a/utils/regtools/lib/Makefile +++ b/utils/regtools/lib/Makefile | |||
@@ -1,18 +1,15 @@ | |||
1 | CC?=gcc | 1 | CC?=gcc |
2 | CXX?=g++ | 2 | CXX?=g++ |
3 | AR?=ar | 3 | AR?=ar |
4 | CFLAGS=-Wall -O2 `xml2-config --cflags` -std=c99 -g -fPIC | 4 | INCLUDE=../include/ |
5 | CXXFLAGS=-Wall -O2 `xml2-config --cflags` -g -fPIC | 5 | CXXFLAGS=-Wall -O2 `xml2-config --cflags` -g -fPIC -I$(INCLUDE) |
6 | LIB=libsocdesc.a | 6 | LIB=libsocdesc.a |
7 | SRC=$(wildcard *.c) | 7 | SRC=$(wildcard *.c) |
8 | SRCXX=$(wildcard *.cpp) | 8 | SRCXX=$(wildcard *.cpp) |
9 | OBJ=$(SRC:.c=.o) $(SRCXX:.cpp=.o) | 9 | OBJ=$(SRCXX:.cpp=.o) |
10 | 10 | ||
11 | all: $(LIB) $(EXEC) | 11 | all: $(LIB) $(EXEC) |
12 | 12 | ||
13 | %.o: %.c | ||
14 | $(CC) $(CFLAGS) -c -o $@ $< | ||
15 | |||
16 | %.o: %.cpp | 13 | %.o: %.cpp |
17 | $(CXX) $(CXXFLAGS) -c -o $@ $< | 14 | $(CXX) $(CXXFLAGS) -c -o $@ $< |
18 | 15 | ||
diff --git a/utils/regtools/lib/formula.cpp b/utils/regtools/lib/formula.cpp new file mode 100644 index 0000000000..1a6b16c773 --- /dev/null +++ b/utils/regtools/lib/formula.cpp | |||
@@ -0,0 +1,214 @@ | |||
1 | #include "soc_desc.hpp" | ||
2 | #include <cstdarg> | ||
3 | #include <cstdio> | ||
4 | |||
5 | using namespace soc_desc; | ||
6 | |||
7 | namespace soc_desc | ||
8 | { | ||
9 | |||
10 | namespace | ||
11 | { | ||
12 | |||
13 | struct formula_evaluator | ||
14 | { | ||
15 | std::string formula; | ||
16 | size_t pos; | ||
17 | error_context_t& ctx; | ||
18 | std::string m_loc; | ||
19 | |||
20 | bool err(const char *fmt, ...) | ||
21 | { | ||
22 | char buffer[256]; | ||
23 | va_list args; | ||
24 | va_start(args, fmt); | ||
25 | vsnprintf(buffer,sizeof(buffer), fmt, args); | ||
26 | va_end(args); | ||
27 | ctx.add(error_t(error_t::FATAL, m_loc, buffer)); | ||
28 | return false; | ||
29 | } | ||
30 | |||
31 | formula_evaluator(const std::string& s, error_context_t& ctx):pos(0),ctx(ctx) | ||
32 | { | ||
33 | for(size_t i = 0; i < s.size(); i++) | ||
34 | if(!isspace(s[i])) | ||
35 | formula.push_back(s[i]); | ||
36 | } | ||
37 | |||
38 | void set_location(const std::string& loc) | ||
39 | { | ||
40 | m_loc = loc; | ||
41 | } | ||
42 | |||
43 | void adv() | ||
44 | { | ||
45 | pos++; | ||
46 | } | ||
47 | |||
48 | char cur() | ||
49 | { | ||
50 | return end() ? 0 : formula[pos]; | ||
51 | } | ||
52 | |||
53 | bool end() | ||
54 | { | ||
55 | return pos >= formula.size(); | ||
56 | } | ||
57 | |||
58 | bool parse_digit(char c, int basis, soc_word_t& res) | ||
59 | { | ||
60 | c = tolower(c); | ||
61 | if(isdigit(c)) | ||
62 | { | ||
63 | res = c - '0'; | ||
64 | return true; | ||
65 | } | ||
66 | if(basis == 16 && isxdigit(c)) | ||
67 | { | ||
68 | res = c + 10 - 'a'; | ||
69 | return true; | ||
70 | } | ||
71 | return false; | ||
72 | } | ||
73 | |||
74 | bool parse_signed(soc_word_t& res) | ||
75 | { | ||
76 | char op = cur(); | ||
77 | if(op == '+' || op == '-') | ||
78 | { | ||
79 | adv(); | ||
80 | if(!parse_signed(res)) | ||
81 | return false; | ||
82 | if(op == '-') | ||
83 | res *= -1; | ||
84 | return true; | ||
85 | } | ||
86 | else if(op == '(') | ||
87 | { | ||
88 | adv(); | ||
89 | if(!parse_expression(res)) | ||
90 | return false; | ||
91 | if(cur() != ')') | ||
92 | return err("expected ')', got '%c'", cur()); | ||
93 | adv(); | ||
94 | return true; | ||
95 | } | ||
96 | else if(isdigit(op)) | ||
97 | { | ||
98 | res = op - '0'; | ||
99 | adv(); | ||
100 | int basis = 10; | ||
101 | if(op == '0' && cur() == 'x') | ||
102 | { | ||
103 | basis = 16; | ||
104 | adv(); | ||
105 | } | ||
106 | soc_word_t digit = 0; | ||
107 | while(parse_digit(cur(), basis, digit)) | ||
108 | { | ||
109 | res = res * basis + digit; | ||
110 | adv(); | ||
111 | } | ||
112 | return true; | ||
113 | } | ||
114 | else if(isalpha(op) || op == '_') | ||
115 | { | ||
116 | std::string name; | ||
117 | while(isalnum(cur()) || cur() == '_') | ||
118 | { | ||
119 | name.push_back(cur()); | ||
120 | adv(); | ||
121 | } | ||
122 | return get_variable(name, res); | ||
123 | } | ||
124 | else | ||
125 | return err("express signed expression, got '%c'", op); | ||
126 | } | ||
127 | |||
128 | bool parse_term(soc_word_t& res) | ||
129 | { | ||
130 | if(!parse_signed(res)) | ||
131 | return false; | ||
132 | while(cur() == '*' || cur() == '/' || cur() == '%') | ||
133 | { | ||
134 | char op = cur(); | ||
135 | adv(); | ||
136 | soc_word_t tmp; | ||
137 | if(!parse_signed(tmp)) | ||
138 | return false; | ||
139 | if(op == '*') | ||
140 | res *= tmp; | ||
141 | else if(tmp != 0) | ||
142 | res = op == '/' ? res / tmp : res % tmp; | ||
143 | else | ||
144 | return err("division by 0"); | ||
145 | } | ||
146 | return true; | ||
147 | } | ||
148 | |||
149 | bool parse_expression(soc_word_t& res) | ||
150 | { | ||
151 | if(!parse_term(res)) | ||
152 | return false; | ||
153 | while(!end() && (cur() == '+' || cur() == '-')) | ||
154 | { | ||
155 | char op = cur(); | ||
156 | adv(); | ||
157 | soc_word_t tmp; | ||
158 | if(!parse_term(tmp)) | ||
159 | return false; | ||
160 | if(op == '+') | ||
161 | res += tmp; | ||
162 | else | ||
163 | res -= tmp; | ||
164 | } | ||
165 | return true; | ||
166 | } | ||
167 | |||
168 | bool parse(soc_word_t& res) | ||
169 | { | ||
170 | bool ok = parse_expression(res); | ||
171 | if(ok && !end()) | ||
172 | err("unexpected character '%c'", cur()); | ||
173 | return ok && end(); | ||
174 | } | ||
175 | |||
176 | virtual bool get_variable(std::string name, soc_word_t& res) | ||
177 | { | ||
178 | return err("unknown variable '%s'", name.c_str()); | ||
179 | } | ||
180 | }; | ||
181 | |||
182 | struct my_evaluator : public formula_evaluator | ||
183 | { | ||
184 | const std::map< std::string, soc_word_t>& var; | ||
185 | |||
186 | my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var, | ||
187 | error_context_t& ctx) | ||
188 | :formula_evaluator(formula, ctx), var(_var) {} | ||
189 | |||
190 | virtual bool get_variable(std::string name, soc_word_t& res) | ||
191 | { | ||
192 | std::map< std::string, soc_word_t>::const_iterator it = var.find(name); | ||
193 | if(it == var.end()) | ||
194 | return formula_evaluator::get_variable(name, res); | ||
195 | else | ||
196 | { | ||
197 | res = it->second; | ||
198 | return true; | ||
199 | } | ||
200 | } | ||
201 | }; | ||
202 | |||
203 | } | ||
204 | |||
205 | bool evaluate_formula(const std::string& formula, | ||
206 | const std::map< std::string, soc_word_t>& var, soc_word_t& result, | ||
207 | const std::string& loc, error_context_t& error) | ||
208 | { | ||
209 | my_evaluator e(formula, var, error); | ||
210 | e.set_location(loc); | ||
211 | return e.parse(result); | ||
212 | } | ||
213 | |||
214 | } // soc_desc | ||
diff --git a/utils/regtools/lib/soc_desc.cpp b/utils/regtools/lib/soc_desc.cpp index 90e4e0eb81..4e0e46f00e 100644 --- a/utils/regtools/lib/soc_desc.cpp +++ b/utils/regtools/lib/soc_desc.cpp | |||
@@ -7,7 +7,7 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2012 by Amaury Pouly | 10 | * Copyright (C) 2014 by Amaury Pouly |
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or | 12 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 13 | * modify it under the terms of the GNU General Public License |
@@ -27,261 +27,429 @@ | |||
27 | #include <string.h> | 27 | #include <string.h> |
28 | #include <algorithm> | 28 | #include <algorithm> |
29 | #include <cctype> | 29 | #include <cctype> |
30 | #include <sstream> | ||
31 | #include <limits> | ||
32 | |||
33 | namespace soc_desc | ||
34 | { | ||
35 | |||
36 | /** | ||
37 | * Parser | ||
38 | */ | ||
30 | 39 | ||
31 | #define XML_CHAR_TO_CHAR(s) ((const char *)(s)) | 40 | #define XML_CHAR_TO_CHAR(s) ((const char *)(s)) |
32 | 41 | ||
33 | #define BEGIN_ATTR_MATCH(attr) \ | 42 | #define BEGIN_ATTR_MATCH(attr) \ |
34 | for(xmlAttr *a = attr; a; a = a->next) { | 43 | for(xmlAttr *a = attr; a; a = a->next) { |
35 | 44 | ||
36 | #define MATCH_X_ATTR(attr_name, hook, ...) \ | 45 | #define MATCH_UNIQUE_ATTR(attr_name, val, has, parse_fn, ctx) \ |
37 | if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ | 46 | if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ |
38 | std::string s; \ | 47 | if(has) \ |
39 | if(!parse_text_attr(a, s) || !hook(s, __VA_ARGS__)) \ | 48 | return parse_not_unique_attr_error(a, ctx); \ |
40 | return false; \ | 49 | has = true; \ |
50 | xmlChar *str = NULL; \ | ||
51 | if(!parse_text_attr_internal(a, str, ctx) || !parse_fn(a, val, str, ctx)) \ | ||
52 | ret = false; \ | ||
41 | } | 53 | } |
42 | 54 | ||
43 | #define SOFT_MATCH_X_ATTR(attr_name, hook, ...) \ | ||
44 | if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ | ||
45 | std::string s; \ | ||
46 | if(parse_text_attr(a, s)) \ | ||
47 | hook(s, __VA_ARGS__); \ | ||
48 | } | ||
49 | |||
50 | #define SOFT_MATCH_SCT_ATTR(attr_name, var) \ | ||
51 | SOFT_MATCH_X_ATTR(attr_name, validate_sct_hook, var) | ||
52 | |||
53 | #define MATCH_TEXT_ATTR(attr_name, var) \ | ||
54 | MATCH_X_ATTR(attr_name, validate_string_hook, var) | ||
55 | |||
56 | #define MATCH_UINT32_ATTR(attr_name, var) \ | ||
57 | MATCH_X_ATTR(attr_name, validate_uint32_hook, var) | ||
58 | |||
59 | #define MATCH_BITRANGE_ATTR(attr_name, first, last) \ | ||
60 | MATCH_X_ATTR(attr_name, validate_bitrange_hook, first, last) | ||
61 | |||
62 | #define END_ATTR_MATCH() \ | 55 | #define END_ATTR_MATCH() \ |
63 | } | 56 | } |
64 | 57 | ||
65 | #define BEGIN_NODE_MATCH(node) \ | 58 | #define BEGIN_NODE_MATCH(node) \ |
66 | for(xmlNode *sub = node; sub; sub = sub->next) { | 59 | for(xmlNode *sub = node; sub; sub = sub->next) { |
67 | 60 | ||
68 | #define MATCH_ELEM_NODE(node_name, array, parse_fn) \ | 61 | #define MATCH_ELEM_NODE(node_name, array, parse_fn, ctx) \ |
69 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ | 62 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ |
70 | array.resize(array.size() + 1); \ | 63 | array.resize(array.size() + 1); \ |
71 | if(!parse_fn(sub, array.back())) \ | 64 | if(!parse_fn(sub, array.back(), ctx)) \ |
72 | return false; \ | 65 | ret = false; \ |
66 | array.back().id = array.size(); \ | ||
73 | } | 67 | } |
74 | 68 | ||
75 | #define SOFT_MATCH_ELEM_NODE(node_name, array, parse_fn) \ | 69 | #define MATCH_TEXT_NODE(node_name, array, parse_fn, ctx) \ |
76 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ | 70 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ |
71 | if(!is_real_text_node(sub)) \ | ||
72 | return parse_not_text_error(sub, ctx); \ | ||
73 | xmlChar *content = xmlNodeGetContent(sub); \ | ||
77 | array.resize(array.size() + 1); \ | 74 | array.resize(array.size() + 1); \ |
78 | if(!parse_fn(sub, array.back())) \ | 75 | ret = ret && parse_fn(sub, array.back(), content, ctx); \ |
79 | array.pop_back(); \ | 76 | xmlFree(content); \ |
77 | } | ||
78 | |||
79 | #define MATCH_UNIQUE_ELEM_NODE(node_name, val, has, parse_fn, ctx) \ | ||
80 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ | ||
81 | if(has) \ | ||
82 | return parse_not_unique_error(sub, ctx); \ | ||
83 | has = true; \ | ||
84 | if(!parse_fn(sub, val, ctx)) \ | ||
85 | ret = false; \ | ||
86 | } | ||
87 | |||
88 | #define MATCH_UNIQUE_TEXT_NODE(node_name, val, has, parse_fn, ctx) \ | ||
89 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ | ||
90 | if(has) \ | ||
91 | return parse_not_unique_error(sub, ctx); \ | ||
92 | if(!is_real_text_node(sub)) \ | ||
93 | return parse_not_text_error(sub, ctx); \ | ||
94 | has = true; \ | ||
95 | xmlChar *content = xmlNodeGetContent(sub); \ | ||
96 | ret = ret && parse_fn(sub, val, content, ctx); \ | ||
97 | xmlFree(content); \ | ||
80 | } | 98 | } |
81 | 99 | ||
82 | #define END_NODE_MATCH() \ | 100 | #define END_NODE_MATCH() \ |
83 | } | 101 | } |
84 | 102 | ||
103 | #define CHECK_HAS(node, node_name, has, ctx) \ | ||
104 | if(!has) \ | ||
105 | ret = ret && parse_missing_error(node, node_name, ctx); | ||
106 | |||
107 | #define CHECK_HAS_ATTR(node, attr_name, has, ctx) \ | ||
108 | if(!has) \ | ||
109 | ret = ret && parse_missing_attr_error(node, attr_name, ctx); | ||
110 | |||
85 | namespace | 111 | namespace |
86 | { | 112 | { |
87 | 113 | ||
88 | bool validate_string_hook(const std::string& str, std::string& s) | 114 | bool is_real_text_node(xmlNode *node) |
89 | { | 115 | { |
90 | s = str; | 116 | for(xmlNode *sub = node->children; sub; sub = sub->next) |
117 | if(sub->type != XML_TEXT_NODE && sub->type != XML_ENTITY_REF_NODE) | ||
118 | return false; | ||
91 | return true; | 119 | return true; |
92 | } | 120 | } |
93 | 121 | ||
94 | bool validate_sct_hook(const std::string& str, soc_reg_flags_t& flags) | 122 | std::string xml_loc(xmlNode *node) |
95 | { | 123 | { |
96 | if(str == "yes") flags |= REG_HAS_SCT; | 124 | std::ostringstream oss; |
97 | else if(str != "no") return false; | 125 | oss << "line " << node->line; |
98 | return true; | 126 | return oss.str(); |
99 | } | 127 | } |
100 | 128 | ||
101 | bool validate_unsigned_long_hook(const std::string& str, unsigned long& s) | 129 | std::string xml_loc(xmlAttr *attr) |
102 | { | 130 | { |
103 | char *end; | 131 | return xml_loc(attr->parent); |
104 | s = strtoul(str.c_str(), &end, 0); | ||
105 | return *end == 0; | ||
106 | } | 132 | } |
107 | 133 | ||
108 | bool validate_uint32_hook(const std::string& str, uint32_t& s) | 134 | template<typename T> |
135 | bool add_error(error_context_t& ctx, error_t::level_t lvl, T *node, | ||
136 | const std::string& msg) | ||
109 | { | 137 | { |
110 | unsigned long u; | 138 | ctx.add(error_t(lvl, xml_loc(node), msg)); |
111 | if(!validate_unsigned_long_hook(str, u)) return false; | 139 | return false; |
112 | #if ULONG_MAX > 0xffffffff | ||
113 | if(u > 0xffffffff) return false; | ||
114 | #endif | ||
115 | s = u; | ||
116 | return true; | ||
117 | } | 140 | } |
118 | 141 | ||
119 | bool validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last) | 142 | template<typename T> |
143 | bool add_fatal(error_context_t& ctx, T *node, const std::string& msg) | ||
120 | { | 144 | { |
121 | unsigned long a, b; | 145 | return add_error(ctx, error_t::FATAL, node, msg); |
122 | size_t sep = str.find(':'); | ||
123 | if(sep == std::string::npos) return false; | ||
124 | if(!validate_unsigned_long_hook(str.substr(0, sep), a)) return false; | ||
125 | if(!validate_unsigned_long_hook(str.substr(sep + 1), b)) return false; | ||
126 | if(a > 31 || b > 31 || a < b) return false; | ||
127 | first = b; | ||
128 | last = a; | ||
129 | return true; | ||
130 | } | 146 | } |
131 | 147 | ||
132 | bool parse_text_attr(xmlAttr *attr, std::string& s) | 148 | template<typename T> |
149 | bool add_warning(error_context_t& ctx, T *node, const std::string& msg) | ||
133 | { | 150 | { |
134 | if(attr->children != attr->last) | 151 | return add_error(ctx, error_t::WARNING, node, msg); |
135 | return false; | ||
136 | if(attr->children->type != XML_TEXT_NODE) | ||
137 | return false; | ||
138 | s = XML_CHAR_TO_CHAR(attr->children->content); | ||
139 | return true; | ||
140 | } | 152 | } |
141 | 153 | ||
142 | bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value) | 154 | bool parse_wrong_version_error(xmlNode *node, error_context_t& ctx) |
143 | { | 155 | { |
144 | BEGIN_ATTR_MATCH(node->properties) | 156 | std::ostringstream oss; |
145 | MATCH_TEXT_ATTR("name", value.name) | 157 | oss << "unknown version, only version " << MAJOR_VERSION << " is supported"; |
146 | MATCH_UINT32_ATTR("value", value.value) | 158 | return add_fatal(ctx, node, oss.str()); |
147 | MATCH_TEXT_ATTR("desc", value.desc) | 159 | } |
148 | END_ATTR_MATCH() | ||
149 | 160 | ||
150 | return true; | 161 | bool parse_not_unique_error(xmlNode *node, error_context_t& ctx) |
162 | { | ||
163 | std::ostringstream oss; | ||
164 | oss << "there must be a unique <" << XML_CHAR_TO_CHAR(node->name) << "> element"; | ||
165 | if(node->parent->name) | ||
166 | oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">"; | ||
167 | else | ||
168 | oss << " at root level"; | ||
169 | return add_fatal(ctx, node, oss.str()); | ||
151 | } | 170 | } |
152 | 171 | ||
153 | bool parse_field_elem(xmlNode *node, soc_reg_field_t& field) | 172 | bool parse_not_unique_attr_error(xmlAttr *attr, error_context_t& ctx) |
154 | { | 173 | { |
155 | BEGIN_ATTR_MATCH(node->properties) | 174 | std::ostringstream oss; |
156 | MATCH_TEXT_ATTR("name", field.name) | 175 | oss << "there must be a unique " << XML_CHAR_TO_CHAR(attr->name) << " attribute"; |
157 | MATCH_BITRANGE_ATTR("bitrange", field.first_bit, field.last_bit) | 176 | oss << " in <" << XML_CHAR_TO_CHAR(attr->parent->name) << ">"; |
158 | MATCH_TEXT_ATTR("desc", field.desc) | 177 | return add_fatal(ctx, attr, oss.str()); |
159 | END_ATTR_MATCH() | 178 | } |
160 | 179 | ||
161 | BEGIN_NODE_MATCH(node->children) | 180 | bool parse_missing_error(xmlNode *node, const char *name, error_context_t& ctx) |
162 | SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem) | 181 | { |
163 | END_NODE_MATCH() | 182 | std::ostringstream oss; |
183 | oss << "missing <" << name << "> element"; | ||
184 | if(node->parent->name) | ||
185 | oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">"; | ||
186 | else | ||
187 | oss << " at root level"; | ||
188 | return add_fatal(ctx, node, oss.str()); | ||
189 | } | ||
164 | 190 | ||
165 | return true; | 191 | bool parse_missing_attr_error(xmlNode *node, const char *name, error_context_t& ctx) |
192 | { | ||
193 | std::ostringstream oss; | ||
194 | oss << "missing " << name << " attribute"; | ||
195 | oss << " in <" << XML_CHAR_TO_CHAR(node->name) << ">"; | ||
196 | return add_fatal(ctx, node, oss.str()); | ||
166 | } | 197 | } |
167 | 198 | ||
168 | bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr) | 199 | bool parse_conflict_error(xmlNode *node, const char *name1, const char *name2, |
200 | error_context_t& ctx) | ||
169 | { | 201 | { |
170 | BEGIN_ATTR_MATCH(node->properties) | 202 | std::ostringstream oss; |
171 | MATCH_TEXT_ATTR("name", addr.name) | 203 | oss << "conflicting <" << name1 << "> and <" << name2 << "> elements"; |
172 | MATCH_UINT32_ATTR("addr", addr.addr) | 204 | if(node->parent->name) |
173 | END_ATTR_MATCH() | 205 | oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">"; |
206 | else | ||
207 | oss << " at root level"; | ||
208 | return add_fatal(ctx, node, oss.str()); | ||
209 | } | ||
174 | 210 | ||
175 | return true; | 211 | bool parse_not_text_error(xmlNode *node, error_context_t& ctx) |
212 | { | ||
213 | return add_fatal(ctx, node, "this is not a text element"); | ||
176 | } | 214 | } |
177 | 215 | ||
178 | bool parse_reg_formula_elem(xmlNode *node, soc_reg_formula_t& formula) | 216 | bool parse_not_text_attr_error(xmlAttr *attr, error_context_t& ctx) |
179 | { | 217 | { |
180 | BEGIN_ATTR_MATCH(node->properties) | 218 | return add_fatal(ctx, attr, "this is not a text attribute"); |
181 | MATCH_TEXT_ATTR("string", formula.string) | 219 | } |
182 | END_ATTR_MATCH() | ||
183 | 220 | ||
184 | formula.type = REG_FORMULA_STRING; | 221 | bool parse_text_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx) |
222 | { | ||
223 | name = XML_CHAR_TO_CHAR(content); | ||
224 | return true; | ||
225 | } | ||
185 | 226 | ||
227 | bool parse_name_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx) | ||
228 | { | ||
229 | name = XML_CHAR_TO_CHAR(content); | ||
230 | if(name.size() == 0) | ||
231 | return add_fatal(ctx, node, "name cannot be empty"); | ||
232 | for(size_t i = 0; i < name.size(); i++) | ||
233 | if(!isalnum(name[i]) && name[i] != '_') | ||
234 | return add_fatal(ctx, node, "name must only contain alphanumeric characters or _"); | ||
186 | return true; | 235 | return true; |
187 | } | 236 | } |
188 | 237 | ||
189 | bool parse_add_trivial_addr(const std::string& str, soc_reg_t& reg) | 238 | template<typename T, typename U> |
239 | bool parse_unsigned_text(U *node, T& res, xmlChar *content, error_context_t& ctx) | ||
190 | { | 240 | { |
191 | soc_reg_addr_t a; | 241 | char *end; |
192 | a.name = reg.name; | 242 | unsigned long uns = strtoul(XML_CHAR_TO_CHAR(content), &end, 0); |
193 | if(!validate_uint32_hook(str, a.addr)) | 243 | if(*end != 0) |
194 | return false; | 244 | return add_fatal(ctx, node, "content must be an unsigned integer"); |
195 | reg.addr.push_back(a); | 245 | res = uns; |
246 | if(res != uns) | ||
247 | return add_fatal(ctx, node, "value does not fit into allowed range"); | ||
196 | return true; | 248 | return true; |
197 | } | 249 | } |
198 | 250 | ||
199 | bool parse_reg_elem(xmlNode *node, soc_reg_t& reg) | 251 | template<typename T> |
252 | bool parse_unsigned_elem(xmlNode *node, T& res, xmlChar *content, error_context_t& ctx) | ||
200 | { | 253 | { |
201 | std::list< soc_reg_formula_t > formulas; | 254 | return parse_unsigned_text(node, res, content, ctx); |
202 | BEGIN_ATTR_MATCH(node->properties) | 255 | } |
203 | MATCH_TEXT_ATTR("name", reg.name) | ||
204 | SOFT_MATCH_SCT_ATTR("sct", reg.flags) | ||
205 | SOFT_MATCH_X_ATTR("addr", parse_add_trivial_addr, reg) | ||
206 | MATCH_TEXT_ATTR("desc", reg.desc) | ||
207 | END_ATTR_MATCH() | ||
208 | 256 | ||
209 | BEGIN_NODE_MATCH(node->children) | 257 | template<typename T> |
210 | MATCH_ELEM_NODE("addr", reg.addr, parse_reg_addr_elem) | 258 | bool parse_unsigned_attr(xmlAttr *attr, T& res, xmlChar *content, error_context_t& ctx) |
211 | MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem) | 259 | { |
212 | MATCH_ELEM_NODE("field", reg.field, parse_field_elem) | 260 | return parse_unsigned_text(attr, res, content, ctx); |
213 | END_NODE_MATCH() | 261 | } |
214 | 262 | ||
215 | if(formulas.size() > 1) | 263 | bool parse_text_attr_internal(xmlAttr *attr, xmlChar*& res, error_context_t& ctx) |
216 | { | 264 | { |
217 | fprintf(stderr, "Only one formula is allowed per register\n"); | 265 | if(attr->children != attr->last) |
218 | return false; | 266 | return false; |
219 | } | 267 | if(attr->children->type != XML_TEXT_NODE) |
220 | if(formulas.size() == 1) | 268 | return parse_not_text_attr_error(attr, ctx); |
221 | reg.formula = formulas.front(); | 269 | res = attr->children->content; |
222 | |||
223 | return true; | 270 | return true; |
224 | } | 271 | } |
225 | 272 | ||
226 | bool parse_dev_addr_elem(xmlNode *node, soc_dev_addr_t& addr) | 273 | bool parse_text_attr(xmlAttr *attr, std::string& res, xmlChar *content, error_context_t& ctx) |
227 | { | 274 | { |
228 | BEGIN_ATTR_MATCH(node->properties) | 275 | res = XML_CHAR_TO_CHAR(content); |
229 | MATCH_TEXT_ATTR("name", addr.name) | ||
230 | MATCH_UINT32_ATTR("addr", addr.addr) | ||
231 | END_ATTR_MATCH() | ||
232 | |||
233 | return true; | 276 | return true; |
234 | } | 277 | } |
235 | 278 | ||
236 | bool parse_dev_elem(xmlNode *node, soc_dev_t& dev) | 279 | bool parse_enum_elem(xmlNode *node, enum_t& reg, error_context_t& ctx) |
237 | { | 280 | { |
238 | BEGIN_ATTR_MATCH(node->properties) | 281 | bool ret = true; |
239 | MATCH_TEXT_ATTR("name", dev.name) | 282 | bool has_name = false, has_value = false, has_desc = false; |
240 | MATCH_TEXT_ATTR("long_name", dev.long_name) | 283 | BEGIN_NODE_MATCH(node->children) |
241 | MATCH_TEXT_ATTR("desc", dev.desc) | 284 | MATCH_UNIQUE_TEXT_NODE("name", reg.name, has_name, parse_name_elem, ctx) |
242 | MATCH_TEXT_ATTR("version", dev.version) | 285 | MATCH_UNIQUE_TEXT_NODE("value", reg.value, has_value, parse_unsigned_elem, ctx) |
243 | END_ATTR_MATCH() | 286 | MATCH_UNIQUE_TEXT_NODE("desc", reg.desc, has_desc, parse_text_elem, ctx) |
287 | END_NODE_MATCH() | ||
288 | CHECK_HAS(node, "name", has_name, ctx) | ||
289 | CHECK_HAS(node, "value", has_value, ctx) | ||
290 | return ret; | ||
291 | } | ||
244 | 292 | ||
293 | bool parse_field_elem(xmlNode *node, field_t& field, error_context_t& ctx) | ||
294 | { | ||
295 | bool ret = true; | ||
296 | bool has_name = false, has_pos = false, has_desc = false, has_width = false; | ||
245 | BEGIN_NODE_MATCH(node->children) | 297 | BEGIN_NODE_MATCH(node->children) |
246 | MATCH_ELEM_NODE("addr", dev.addr, parse_dev_addr_elem) | 298 | MATCH_UNIQUE_TEXT_NODE("name", field.name, has_name, parse_name_elem, ctx) |
247 | MATCH_ELEM_NODE("reg", dev.reg, parse_reg_elem) | 299 | MATCH_UNIQUE_TEXT_NODE("position", field.pos, has_pos, parse_unsigned_elem, ctx) |
300 | MATCH_UNIQUE_TEXT_NODE("width", field.width, has_width, parse_unsigned_elem, ctx) | ||
301 | MATCH_UNIQUE_TEXT_NODE("desc", field.desc, has_desc, parse_text_elem, ctx) | ||
302 | MATCH_ELEM_NODE("enum", field.enum_, parse_enum_elem, ctx) | ||
248 | END_NODE_MATCH() | 303 | END_NODE_MATCH() |
304 | CHECK_HAS(node, "name", has_name, ctx) | ||
305 | CHECK_HAS(node, "position", has_pos, ctx) | ||
306 | if(!has_width) | ||
307 | field.width = 1; | ||
308 | return ret; | ||
309 | } | ||
249 | 310 | ||
250 | return true; | 311 | bool parse_register_elem(xmlNode *node, register_t& reg, error_context_t& ctx) |
312 | { | ||
313 | bool ret = true; | ||
314 | bool has_width = false; | ||
315 | BEGIN_NODE_MATCH(node->children) | ||
316 | MATCH_UNIQUE_TEXT_NODE("width", reg.width, has_width, parse_unsigned_elem, ctx) | ||
317 | MATCH_ELEM_NODE("field", reg.field, parse_field_elem, ctx) | ||
318 | END_NODE_MATCH() | ||
319 | if(!has_width) | ||
320 | reg.width = 32; | ||
321 | return ret; | ||
251 | } | 322 | } |
252 | 323 | ||
253 | bool parse_soc_elem(xmlNode *node, soc_t& soc) | 324 | bool parse_formula_elem(xmlNode *node, range_t& range, error_context_t& ctx) |
254 | { | 325 | { |
326 | bool ret = true; | ||
327 | bool has_var = false; | ||
255 | BEGIN_ATTR_MATCH(node->properties) | 328 | BEGIN_ATTR_MATCH(node->properties) |
256 | MATCH_TEXT_ATTR("name", soc.name) | 329 | MATCH_UNIQUE_ATTR("variable", range.variable, has_var, parse_text_attr, ctx) |
257 | MATCH_TEXT_ATTR("desc", soc.desc) | 330 | END_NODE_MATCH() |
258 | END_ATTR_MATCH() | 331 | CHECK_HAS_ATTR(node, "variable", has_var, ctx) |
332 | return ret; | ||
333 | } | ||
259 | 334 | ||
335 | bool parse_range_elem(xmlNode *node, range_t& range, error_context_t& ctx) | ||
336 | { | ||
337 | bool ret = true; | ||
338 | bool has_first = false, has_count = false, has_stride = false, has_base = false; | ||
339 | bool has_formula = false, has_formula_attr = false; | ||
260 | BEGIN_NODE_MATCH(node->children) | 340 | BEGIN_NODE_MATCH(node->children) |
261 | MATCH_ELEM_NODE("dev", soc.dev, parse_dev_elem) | 341 | MATCH_UNIQUE_TEXT_NODE("first", range.first, has_first, parse_unsigned_elem, ctx) |
342 | MATCH_UNIQUE_TEXT_NODE("count", range.count, has_count, parse_unsigned_elem, ctx) | ||
343 | MATCH_UNIQUE_TEXT_NODE("base", range.base, has_base, parse_unsigned_elem, ctx) | ||
344 | MATCH_UNIQUE_TEXT_NODE("stride", range.stride, has_stride, parse_unsigned_elem, ctx) | ||
345 | MATCH_UNIQUE_ELEM_NODE("formula", range, has_formula_attr, parse_formula_elem, ctx) | ||
346 | MATCH_UNIQUE_TEXT_NODE("formula", range.formula, has_formula, parse_text_elem, ctx) | ||
262 | END_NODE_MATCH() | 347 | END_NODE_MATCH() |
348 | CHECK_HAS(node, "first", has_first, ctx) | ||
349 | CHECK_HAS(node, "count", has_count, ctx) | ||
350 | if(!has_base && !has_formula) | ||
351 | ret = ret && parse_missing_error(node, "base> or <formula", ctx); | ||
352 | if(has_base && has_formula) | ||
353 | return parse_conflict_error(node, "base", "formula", ctx); | ||
354 | if(has_base) | ||
355 | CHECK_HAS(node, "stride", has_stride, ctx) | ||
356 | if(has_stride && !has_base) | ||
357 | ret = ret && parse_conflict_error(node, "stride", "formula", ctx); | ||
358 | if(has_stride) | ||
359 | range.type = range_t::STRIDE; | ||
360 | else | ||
361 | range.type = range_t::FORMULA; | ||
362 | return ret; | ||
363 | } | ||
263 | 364 | ||
264 | return true; | 365 | bool parse_instance_elem(xmlNode *node, instance_t& inst, error_context_t& ctx) |
366 | { | ||
367 | bool ret = true; | ||
368 | bool has_name = false, has_title = false, has_desc = false, has_range = false; | ||
369 | bool has_address = false; | ||
370 | BEGIN_NODE_MATCH(node->children) | ||
371 | MATCH_UNIQUE_TEXT_NODE("name", inst.name, has_name, parse_name_elem, ctx) | ||
372 | MATCH_UNIQUE_TEXT_NODE("title", inst.title, has_title, parse_text_elem, ctx) | ||
373 | MATCH_UNIQUE_TEXT_NODE("desc", inst.desc, has_desc, parse_text_elem, ctx) | ||
374 | MATCH_UNIQUE_TEXT_NODE("address", inst.addr, has_address, parse_unsigned_elem, ctx) | ||
375 | MATCH_UNIQUE_ELEM_NODE("range", inst.range, has_range, parse_range_elem, ctx) | ||
376 | END_NODE_MATCH() | ||
377 | CHECK_HAS(node, "name", has_name, ctx) | ||
378 | if(!has_address && !has_range) | ||
379 | ret = ret && parse_missing_error(node, "address> or <range", ctx); | ||
380 | if(has_address && has_range) | ||
381 | ret = ret && parse_conflict_error(node, "address", "range", ctx); | ||
382 | if(has_address) | ||
383 | inst.type = instance_t::SINGLE; | ||
384 | else | ||
385 | inst.type = instance_t::RANGE; | ||
386 | return ret; | ||
265 | } | 387 | } |
266 | 388 | ||
267 | bool parse_root_elem(xmlNode *node, soc_t& soc) | 389 | bool parse_node_elem(xmlNode *node_, node_t& node, error_context_t& ctx) |
268 | { | 390 | { |
269 | std::vector< soc_t > socs; | 391 | bool ret = true; |
270 | BEGIN_NODE_MATCH(node) | 392 | register_t reg; |
271 | MATCH_ELEM_NODE("soc", socs, parse_soc_elem) | 393 | bool has_title = false, has_desc = false, has_register = false, has_name = false; |
394 | BEGIN_NODE_MATCH(node_->children) | ||
395 | MATCH_UNIQUE_TEXT_NODE("name", node.name, has_name, parse_name_elem, ctx) | ||
396 | MATCH_UNIQUE_TEXT_NODE("title", node.title, has_title, parse_text_elem, ctx) | ||
397 | MATCH_UNIQUE_TEXT_NODE("desc", node.desc, has_desc, parse_text_elem, ctx) | ||
398 | MATCH_UNIQUE_ELEM_NODE("register", reg, has_register, parse_register_elem, ctx) | ||
399 | MATCH_ELEM_NODE("node", node.node, parse_node_elem, ctx) | ||
400 | MATCH_ELEM_NODE("instance", node.instance, parse_instance_elem, ctx) | ||
272 | END_NODE_MATCH() | 401 | END_NODE_MATCH() |
273 | if(socs.size() != 1) | 402 | CHECK_HAS(node_, "name", has_name, ctx) |
403 | CHECK_HAS(node_, "instance", !node.instance.empty(), ctx) | ||
404 | if(has_register) | ||
405 | node.register_.push_back(reg); | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | bool parse_soc_elem(xmlNode *node, soc_t& soc, error_context_t& ctx) | ||
410 | { | ||
411 | bool ret = true; | ||
412 | bool has_name = false, has_title = false, has_desc = false, has_version = false; | ||
413 | bool has_isa = false; | ||
414 | BEGIN_NODE_MATCH(node->children) | ||
415 | MATCH_UNIQUE_TEXT_NODE("name", soc.name, has_name, parse_name_elem, ctx) | ||
416 | MATCH_UNIQUE_TEXT_NODE("title", soc.title, has_title, parse_text_elem, ctx) | ||
417 | MATCH_UNIQUE_TEXT_NODE("desc", soc.desc, has_desc, parse_text_elem, ctx) | ||
418 | MATCH_UNIQUE_TEXT_NODE("version", soc.version, has_version, parse_text_elem, ctx) | ||
419 | MATCH_UNIQUE_TEXT_NODE("isa", soc.isa, has_isa, parse_text_elem, ctx) | ||
420 | MATCH_TEXT_NODE("author", soc.author, parse_text_elem, ctx) | ||
421 | MATCH_ELEM_NODE("node", soc.node, parse_node_elem, ctx) | ||
422 | END_NODE_MATCH() | ||
423 | CHECK_HAS(node, "name", has_name, ctx) | ||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | bool parse_root_elem(xmlNode *node, soc_t& soc, error_context_t& ctx) | ||
428 | { | ||
429 | size_t ver = 0; | ||
430 | bool ret = true; | ||
431 | bool has_soc = false, has_version = false; | ||
432 | BEGIN_ATTR_MATCH(node->properties) | ||
433 | MATCH_UNIQUE_ATTR("version", ver, has_version, parse_unsigned_attr, ctx) | ||
434 | END_ATTR_MATCH() | ||
435 | if(!has_version) | ||
274 | { | 436 | { |
275 | fprintf(stderr, "A description file must contain exactly one soc element\n"); | 437 | ctx.add(error_t(error_t::FATAL, xml_loc(node), "no version attribute, is this a v1 file ?")); |
276 | return false; | 438 | return false; |
277 | } | 439 | } |
278 | soc = socs[0]; | 440 | if(ver != MAJOR_VERSION) |
279 | return true; | 441 | return parse_wrong_version_error(node, ctx); |
442 | BEGIN_NODE_MATCH(node) | ||
443 | MATCH_UNIQUE_ELEM_NODE("soc", soc, has_soc, parse_soc_elem, ctx) | ||
444 | END_NODE_MATCH() | ||
445 | CHECK_HAS(node, "soc", has_soc, ctx) | ||
446 | return ret; | ||
280 | } | 447 | } |
281 | 448 | ||
282 | } | 449 | } |
283 | 450 | ||
284 | bool soc_desc_parse_xml(const std::string& filename, soc_t& socs) | 451 | bool parse_xml(const std::string& filename, soc_t& soc, |
452 | error_context_t& error_ctx) | ||
285 | { | 453 | { |
286 | LIBXML_TEST_VERSION | 454 | LIBXML_TEST_VERSION |
287 | 455 | ||
@@ -290,156 +458,207 @@ bool soc_desc_parse_xml(const std::string& filename, soc_t& socs) | |||
290 | return false; | 458 | return false; |
291 | 459 | ||
292 | xmlNodePtr root_element = xmlDocGetRootElement(doc); | 460 | xmlNodePtr root_element = xmlDocGetRootElement(doc); |
293 | bool ret = parse_root_elem(root_element, socs); | 461 | bool ret = parse_root_elem(root_element, soc, error_ctx); |
294 | 462 | ||
295 | xmlFreeDoc(doc); | 463 | xmlFreeDoc(doc); |
296 | 464 | ||
297 | return ret; | 465 | return ret; |
298 | } | 466 | } |
299 | 467 | ||
468 | /** | ||
469 | * Producer | ||
470 | */ | ||
471 | |||
300 | namespace | 472 | namespace |
301 | { | 473 | { |
302 | 474 | ||
303 | int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field) | 475 | #define SAFE(x) \ |
476 | do{ \ | ||
477 | if((x) < 0) { \ | ||
478 | std::ostringstream oss; \ | ||
479 | oss << __FILE__ << ":" << __LINE__; \ | ||
480 | ctx.add(error_t(error_t::FATAL, oss.str(), "write error")); \ | ||
481 | return -1; \ | ||
482 | } \ | ||
483 | }while(0) | ||
484 | |||
485 | int produce_range(xmlTextWriterPtr writer, const range_t& range, error_context_t& ctx) | ||
304 | { | 486 | { |
305 | #define SAFE(x) if((x) < 0) return -1; | 487 | /* <range> */ |
306 | /* <field> */ | 488 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "range")); |
307 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); | 489 | /* <first/> */ |
308 | /* name */ | 490 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "first", "%lu", range.first)); |
309 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); | 491 | /* <count/> */ |
310 | /* desc */ | 492 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); |
311 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); | 493 | /* <base/><stride/> */ |
312 | /* bitrange */ | 494 | if(range.type == range_t::STRIDE) |
313 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d", | ||
314 | field.last_bit, field.first_bit)); | ||
315 | /* values */ | ||
316 | for(size_t i = 0; i < field.value.size(); i++) | ||
317 | { | 495 | { |
318 | /* <value> */ | 496 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base)); |
319 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value")); | 497 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride)); |
320 | /* name */ | ||
321 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str())); | ||
322 | /* value */ | ||
323 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value)); | ||
324 | /* name */ | ||
325 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str())); | ||
326 | /* </value> */ | ||
327 | SAFE(xmlTextWriterEndElement(writer)); | ||
328 | } | 498 | } |
329 | /* </field> */ | 499 | /* <formula> */ |
330 | SAFE(xmlTextWriterEndElement(writer)); | 500 | else if(range.type == range_t::FORMULA) |
331 | #undef SAFE | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg) | ||
336 | { | ||
337 | #define SAFE(x) if((x) < 0) return -1; | ||
338 | /* <reg> */ | ||
339 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg")); | ||
340 | /* name */ | ||
341 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str())); | ||
342 | /* name */ | ||
343 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str())); | ||
344 | /* flags */ | ||
345 | if(reg.flags & REG_HAS_SCT) | ||
346 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes")); | ||
347 | /* formula */ | ||
348 | if(reg.formula.type != REG_FORMULA_NONE) | ||
349 | { | 501 | { |
350 | /* <formula> */ | 502 | /* <formula> */ |
351 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula")); | 503 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula")); |
352 | switch(reg.formula.type) | 504 | /* variable */ |
353 | { | 505 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "variable", BAD_CAST range.variable.c_str())); |
354 | case REG_FORMULA_STRING: | 506 | /* content */ |
355 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string", | 507 | SAFE(xmlTextWriterWriteString(writer, BAD_CAST range.formula.c_str())); |
356 | BAD_CAST reg.formula.string.c_str())); | ||
357 | break; | ||
358 | default: | ||
359 | break; | ||
360 | } | ||
361 | /* </formula> */ | 508 | /* </formula> */ |
362 | SAFE(xmlTextWriterEndElement(writer)); | 509 | SAFE(xmlTextWriterEndElement(writer)); |
363 | } | 510 | } |
364 | /* addresses */ | 511 | /* </range> */ |
365 | for(size_t i = 0; i < reg.addr.size(); i++) | 512 | SAFE(xmlTextWriterEndElement(writer)); |
366 | { | 513 | |
367 | /* <addr> */ | 514 | return 0; |
368 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); | 515 | } |
369 | /* name */ | 516 | |
370 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str())); | 517 | int produce_instance(xmlTextWriterPtr writer, const instance_t& inst, error_context_t& ctx) |
371 | /* addr */ | 518 | { |
372 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr)); | 519 | /* <instance> */ |
373 | /* </addr> */ | 520 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "instance")); |
374 | SAFE(xmlTextWriterEndElement(writer)); | 521 | /* <name/> */ |
375 | } | 522 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST inst.name.c_str())); |
523 | /* <title/> */ | ||
524 | if(!inst.title.empty()) | ||
525 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST inst.title.c_str())); | ||
526 | /* <desc/> */ | ||
527 | if(!inst.desc.empty()) | ||
528 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST inst.desc.c_str())); | ||
529 | /* <address/> */ | ||
530 | if(inst.type == instance_t::SINGLE) | ||
531 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", inst.addr)); | ||
532 | /* <range/> */ | ||
533 | else if(inst.type == instance_t::RANGE) | ||
534 | SAFE(produce_range(writer, inst.range, ctx)); | ||
535 | /* </instance> */ | ||
536 | SAFE(xmlTextWriterEndElement(writer)); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | int produce_enum(xmlTextWriterPtr writer, const enum_t& enum_, error_context_t& ctx) | ||
541 | { | ||
542 | /* <enum> */ | ||
543 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "enum")); | ||
544 | /* <name/> */ | ||
545 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST enum_.name.c_str())); | ||
546 | /* <desc/> */ | ||
547 | if(!enum_.desc.empty()) | ||
548 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST enum_.desc.c_str())); | ||
549 | /* <value/> */ | ||
550 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "value", "0x%x", enum_.value)); | ||
551 | /* </enum> */ | ||
552 | SAFE(xmlTextWriterEndElement(writer)); | ||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | int produce_field(xmlTextWriterPtr writer, const field_t& field, error_context_t& ctx) | ||
557 | { | ||
558 | /* <field> */ | ||
559 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); | ||
560 | /* <name/> */ | ||
561 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); | ||
562 | /* <desc/> */ | ||
563 | if(!field.desc.empty()) | ||
564 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); | ||
565 | /* <position/> */ | ||
566 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "position", "%lu", field.pos)); | ||
567 | /* <width/> */ | ||
568 | if(field.width != 1) | ||
569 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", field.width)); | ||
570 | /* enums */ | ||
571 | for(size_t i = 0; i < field.enum_.size(); i++) | ||
572 | SAFE(produce_enum(writer, field.enum_[i], ctx)); | ||
573 | /* </field> */ | ||
574 | SAFE(xmlTextWriterEndElement(writer)); | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | int produce_register(xmlTextWriterPtr writer, const register_t& reg, error_context_t& ctx) | ||
579 | { | ||
580 | /* <register> */ | ||
581 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "register")); | ||
582 | /* <width/> */ | ||
583 | if(reg.width != 32) | ||
584 | SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", reg.width)); | ||
376 | /* fields */ | 585 | /* fields */ |
377 | for(size_t i = 0; i < reg.field.size(); i++) | 586 | for(size_t i = 0; i < reg.field.size(); i++) |
378 | produce_field(writer, reg.field[i]); | 587 | SAFE(produce_field(writer, reg.field[i], ctx)); |
379 | /* </reg> */ | 588 | /* </register> */ |
380 | SAFE(xmlTextWriterEndElement(writer)); | 589 | SAFE(xmlTextWriterEndElement(writer)); |
381 | #undef SAFE | ||
382 | return 0; | 590 | return 0; |
383 | } | 591 | } |
384 | 592 | ||
385 | int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev) | 593 | int produce_node(xmlTextWriterPtr writer, const node_t& node, error_context_t& ctx) |
386 | { | 594 | { |
387 | #define SAFE(x) if((x) < 0) return -1; | 595 | /* <node> */ |
388 | /* <dev> */ | 596 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "node")); |
389 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev")); | 597 | /* <name/> */ |
390 | /* name */ | 598 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST node.name.c_str())); |
391 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str())); | 599 | /* <title/> */ |
392 | /* long_name */ | 600 | if(!node.title.empty()) |
393 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str())); | 601 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST node.title.c_str())); |
394 | /* desc */ | 602 | /* <desc/> */ |
395 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str())); | 603 | if(!node.desc.empty()) |
396 | /* version */ | 604 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST node.desc.c_str())); |
397 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str())); | 605 | /* instances */ |
398 | /* addresses */ | 606 | for(size_t i = 0; i < node.instance.size(); i++) |
399 | for(size_t i = 0; i < dev.addr.size(); i++) | 607 | SAFE(produce_instance(writer, node.instance[i], ctx)); |
400 | { | 608 | /* register */ |
401 | /* <addr> */ | 609 | for(size_t i = 0; i < node.register_.size(); i++) |
402 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); | 610 | SAFE(produce_register(writer, node.register_[i], ctx)); |
403 | /* name */ | 611 | /* nodes */ |
404 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str())); | 612 | for(size_t i = 0; i < node.node.size(); i++) |
405 | /* addr */ | 613 | SAFE(produce_node(writer, node.node[i], ctx)); |
406 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr)); | 614 | /* </node> */ |
407 | /* </addr> */ | ||
408 | SAFE(xmlTextWriterEndElement(writer)); | ||
409 | } | ||
410 | /* registers */ | ||
411 | for(size_t i = 0; i < dev.reg.size(); i++) | ||
412 | produce_reg(writer, dev.reg[i]); | ||
413 | /* </dev> */ | ||
414 | SAFE(xmlTextWriterEndElement(writer)); | 615 | SAFE(xmlTextWriterEndElement(writer)); |
415 | #undef SAFE | ||
416 | return 0; | 616 | return 0; |
417 | } | 617 | } |
418 | 618 | ||
619 | #undef SAFE | ||
620 | |||
419 | } | 621 | } |
420 | 622 | ||
421 | bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc) | 623 | bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& ctx) |
422 | { | 624 | { |
423 | LIBXML_TEST_VERSION | 625 | LIBXML_TEST_VERSION |
424 | 626 | ||
627 | std::ostringstream oss; | ||
425 | xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0); | 628 | xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0); |
426 | if(writer == NULL) | 629 | if(writer == NULL) |
427 | return false; | 630 | return false; |
428 | #define SAFE(x) if((x) < 0) goto Lerr | 631 | #define SAFE(x) do{if((x) < 0) goto Lerr;}while(0) |
429 | SAFE(xmlTextWriterSetIndent(writer, 1)); | 632 | SAFE(xmlTextWriterSetIndent(writer, 1)); |
430 | SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " ")); | 633 | SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " ")); |
431 | /* <xml> */ | 634 | /* <xml> */ |
432 | SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL)); | 635 | SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL)); |
433 | /* <soc> */ | 636 | /* <soc> */ |
434 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc")); | 637 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc")); |
435 | /* name */ | 638 | /* version */ |
436 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str())); | 639 | oss << MAJOR_VERSION; |
437 | /* desc */ | 640 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST oss.str().c_str())); |
438 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str())); | 641 | /* <name/> */ |
439 | /* devices */ | 642 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST soc.name.c_str())); |
440 | for(size_t i = 0; i < soc.dev.size(); i++) | 643 | /* <title/> */ |
441 | SAFE(produce_dev(writer, soc.dev[i])); | 644 | if(!soc.title.empty()) |
442 | /* end <soc> */ | 645 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST soc.title.c_str())); |
646 | /* <desc/> */ | ||
647 | if(!soc.desc.empty()) | ||
648 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str())); | ||
649 | /* <author/> */ | ||
650 | for(size_t i = 0; i < soc.author.size(); i++) | ||
651 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "author", BAD_CAST soc.author[i].c_str())); | ||
652 | /* <isa/> */ | ||
653 | if(!soc.isa.empty()) | ||
654 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "isa", BAD_CAST soc.isa.c_str())); | ||
655 | /* <version/> */ | ||
656 | if(!soc.version.empty()) | ||
657 | SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "version", BAD_CAST soc.version.c_str())); | ||
658 | /* nodes */ | ||
659 | for(size_t i = 0; i < soc.node.size(); i++) | ||
660 | SAFE(produce_node(writer, soc.node[i], ctx)); | ||
661 | /* </soc> */ | ||
443 | SAFE(xmlTextWriterEndElement(writer)); | 662 | SAFE(xmlTextWriterEndElement(writer)); |
444 | /* </xml> */ | 663 | /* </xml> */ |
445 | SAFE(xmlTextWriterEndDocument(writer)); | 664 | SAFE(xmlTextWriterEndDocument(writer)); |
@@ -451,520 +670,500 @@ Lerr: | |||
451 | return false; | 670 | return false; |
452 | } | 671 | } |
453 | 672 | ||
454 | namespace | 673 | /** |
674 | * soc_ref_t | ||
675 | */ | ||
676 | |||
677 | soc_ref_t::soc_ref_t():m_soc(0) | ||
455 | { | 678 | { |
679 | } | ||
456 | 680 | ||
457 | struct soc_sorter | 681 | soc_ref_t::soc_ref_t(soc_t *soc):m_soc(soc) |
458 | { | 682 | { |
459 | bool operator()(const soc_dev_t& a, const soc_dev_t& b) const | 683 | } |
460 | { | ||
461 | return a.name < b.name; | ||
462 | } | ||
463 | 684 | ||
464 | bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const | 685 | bool soc_ref_t::valid() const |
465 | { | 686 | { |
466 | return a.name < b.name; | 687 | return get() != 0; |
467 | } | 688 | } |
468 | 689 | ||
469 | bool operator()(const soc_reg_t& a, const soc_reg_t& b) const | 690 | soc_t *soc_ref_t::get() const |
470 | { | 691 | { |
471 | soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0; | 692 | return m_soc; |
472 | soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0; | 693 | } |
473 | return aa < ab; | ||
474 | } | ||
475 | 694 | ||
476 | bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const | 695 | bool soc_ref_t::operator==(const soc_ref_t& ref) const |
477 | { | 696 | { |
478 | return a.addr < b.addr; | 697 | return m_soc == ref.m_soc; |
479 | } | 698 | } |
480 | 699 | ||
481 | bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const | 700 | node_ref_t soc_ref_t::root() const |
482 | { | 701 | { |
483 | return a.last_bit > b.last_bit; | 702 | return node_ref_t(*this); |
484 | } | 703 | } |
485 | 704 | ||
486 | bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const | 705 | node_inst_t soc_ref_t::root_inst() const |
487 | { | 706 | { |
488 | return a.value < b.value; | 707 | return node_inst_t(*this); |
489 | } | 708 | } |
490 | }; | ||
491 | 709 | ||
492 | void normalize(soc_reg_field_t& field) | 710 | /** |
711 | * node_ref_t */ | ||
712 | |||
713 | node_ref_t::node_ref_t(soc_ref_t soc):m_soc(soc) | ||
493 | { | 714 | { |
494 | std::sort(field.value.begin(), field.value.end(), soc_sorter()); | ||
495 | } | 715 | } |
496 | 716 | ||
497 | void normalize(soc_reg_t& reg) | 717 | node_ref_t::node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path) |
718 | :m_soc(soc), m_path(path) | ||
498 | { | 719 | { |
499 | std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter()); | ||
500 | std::sort(reg.field.begin(), reg.field.end(), soc_sorter()); | ||
501 | for(size_t i = 0; i < reg.field.size(); i++) | ||
502 | normalize(reg.field[i]); | ||
503 | } | 720 | } |
504 | 721 | ||
505 | void normalize(soc_dev_t& dev) | 722 | node_ref_t::node_ref_t() |
506 | { | 723 | { |
507 | std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter()); | ||
508 | std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter()); | ||
509 | for(size_t i = 0; i < dev.reg.size(); i++) | ||
510 | normalize(dev.reg[i]); | ||
511 | } | 724 | } |
512 | 725 | ||
726 | bool node_ref_t::valid() const | ||
727 | { | ||
728 | return (m_soc.valid() && is_root()) || get() != 0; | ||
513 | } | 729 | } |
514 | 730 | ||
515 | void soc_desc_normalize(soc_t& soc) | 731 | bool node_ref_t::is_root() const |
516 | { | 732 | { |
517 | std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter()); | 733 | return m_path.empty(); |
518 | for(size_t i = 0; i < soc.dev.size(); i++) | ||
519 | normalize(soc.dev[i]); | ||
520 | } | 734 | } |
521 | 735 | ||
522 | namespace | 736 | namespace |
523 | { | 737 | { |
524 | soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what) | ||
525 | { | ||
526 | soc_error_t err; | ||
527 | err.level = lvl; | ||
528 | err.location = at; | ||
529 | err.message = what; | ||
530 | return err; | ||
531 | } | ||
532 | 738 | ||
533 | soc_error_t make_warning(std::string at, std::string what) | 739 | std::vector< node_t > *get_children(node_ref_t node) |
534 | { | 740 | { |
535 | return make_error(SOC_ERROR_WARNING, at, what); | 741 | if(node.is_root()) |
536 | } | 742 | return &node.soc().get()->node; |
537 | 743 | node_t *n = node.get(); | |
538 | soc_error_t make_fatal(std::string at, std::string what) | 744 | return n == 0 ? 0 : &n->node; |
539 | { | 745 | } |
540 | return make_error(SOC_ERROR_FATAL, at, what); | ||
541 | } | ||
542 | 746 | ||
543 | soc_error_t prefix(soc_error_t err, const std::string& prefix_at) | 747 | node_t *get_child(std::vector< node_t > *nodes, soc_id_t id) |
544 | { | 748 | { |
545 | err.location = prefix_at + "." + err.location; | 749 | if(nodes == 0) |
546 | return err; | 750 | return 0; |
547 | } | 751 | for(size_t i = 0; i < nodes->size(); i++) |
752 | if((*nodes)[i].id == id) | ||
753 | return &(*nodes)[i]; | ||
754 | return 0; | ||
755 | } | ||
548 | 756 | ||
549 | void add_errors(std::vector< soc_error_t >& errors, | 757 | node_t *get_child(std::vector< node_t > *nodes, const std::string& name) |
550 | const std::vector< soc_error_t >& new_errors, const std::string& prefix_at) | 758 | { |
551 | { | 759 | if(nodes == 0) |
552 | for(size_t i = 0; i < new_errors.size(); i++) | 760 | return 0; |
553 | errors.push_back(prefix(new_errors[i], prefix_at)); | 761 | for(size_t i = 0; i < nodes->size(); i++) |
554 | } | 762 | if((*nodes)[i].name == name) |
763 | return &(*nodes)[i]; | ||
764 | return 0; | ||
765 | } | ||
555 | 766 | ||
556 | std::vector< soc_error_t > no_error() | 767 | } |
557 | { | ||
558 | std::vector< soc_error_t > s; | ||
559 | return s; | ||
560 | } | ||
561 | 768 | ||
562 | std::vector< soc_error_t > one_error(const soc_error_t& err) | 769 | /* NOTE: valid() is implemented using get() != 0, so don't use it in get() ! */ |
770 | node_t *node_ref_t::get() const | ||
771 | { | ||
772 | if(!soc().valid()) | ||
773 | return 0; | ||
774 | /* we could do it recursively but it would make plenty of copies */ | ||
775 | node_t *n = 0; | ||
776 | std::vector< node_t > *nodes = &soc().get()->node; | ||
777 | for(size_t i = 0; i < m_path.size(); i++) | ||
563 | { | 778 | { |
564 | std::vector< soc_error_t > s; | 779 | n = get_child(nodes, m_path[i]); |
565 | s.push_back(err); | 780 | if(n == 0) |
566 | return s; | 781 | return 0; |
782 | nodes = &n->node; | ||
567 | } | 783 | } |
784 | return n; | ||
785 | } | ||
568 | 786 | ||
569 | bool name_valid(char c) | 787 | soc_ref_t node_ref_t::soc() const |
570 | { | 788 | { |
571 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || | 789 | return m_soc; |
572 | (c >= 'A' && c <= 'Z') || c == '_'; | 790 | } |
573 | } | ||
574 | 791 | ||
575 | bool name_valid(const std::string& s) | 792 | node_ref_t node_ref_t::parent() const |
576 | { | 793 | { |
577 | for(size_t i = 0; i < s.size(); i++) | 794 | std::vector< soc_id_t > path = m_path; |
578 | if(!name_valid(s[i])) | 795 | if(!path.empty()) |
579 | return false; | 796 | path.pop_back(); |
580 | return true; | 797 | return node_ref_t(m_soc, path); |
581 | } | ||
582 | } | 798 | } |
583 | 799 | ||
584 | std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive) | 800 | register_ref_t node_ref_t::reg() const |
585 | { | 801 | { |
586 | (void) recursive; | 802 | node_t *n = get(); |
587 | if(name.size() == 0) | 803 | if(n == 0) |
588 | return one_error(make_fatal(name, "empty name")); | 804 | return register_ref_t(); |
589 | else if(!name_valid(name)) | 805 | if(n->register_.empty()) |
590 | return one_error(make_fatal(name, "invalid name")); | 806 | return parent().reg(); |
591 | else | 807 | else |
592 | return no_error(); | 808 | return register_ref_t(*this); |
593 | } | 809 | } |
594 | 810 | ||
595 | std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive) | 811 | node_ref_t node_ref_t::child(const std::string& name) const |
596 | { | 812 | { |
597 | std::vector< soc_error_t > err; | 813 | /* check the node exists */ |
598 | std::string at(name); | 814 | node_t *n = get_child(get_children(*this), name); |
599 | if(name.size() == 0) | 815 | if(n == 0) |
600 | err.push_back(make_fatal(at, "empty name")); | 816 | return node_ref_t(); |
601 | else if(!name_valid(name)) | 817 | std::vector< soc_id_t > path = m_path; |
602 | err.push_back(make_fatal(at, "invalid name")); | 818 | path.push_back(n->id); |
603 | if(last_bit > 31) | 819 | return node_ref_t(m_soc, path); |
604 | err.push_back(make_fatal(at, "last bit is greater than 31")); | 820 | } |
605 | if(first_bit > last_bit) | 821 | |
606 | err.push_back(make_fatal(at, "last bit is greater than first bit")); | 822 | std::vector< node_ref_t > node_ref_t::children() const |
607 | for(size_t i = 0; i < value.size(); i++) | 823 | { |
824 | std::vector< node_ref_t > nodes; | ||
825 | std::vector< node_t > *children = get_children(*this); | ||
826 | if(children == 0) | ||
827 | return nodes; | ||
828 | for(size_t i = 0; i < children->size(); i++) | ||
608 | { | 829 | { |
609 | for(size_t j = 0; j < value.size(); j++) | 830 | std::vector< soc_id_t > path = m_path; |
831 | path.push_back((*children)[i].id); | ||
832 | nodes.push_back(node_ref_t(m_soc, path)); | ||
833 | } | ||
834 | return nodes; | ||
835 | } | ||
836 | |||
837 | std::vector< std::string > node_ref_t::path() const | ||
838 | { | ||
839 | std::vector< std::string > path; | ||
840 | if(!soc().valid()) | ||
841 | return path; | ||
842 | /* we could do it recursively but this is more efficient */ | ||
843 | node_t *n = 0; | ||
844 | std::vector< node_t > *nodes = &soc().get()->node; | ||
845 | for(size_t i = 0; i < m_path.size(); i++) | ||
846 | { | ||
847 | n = get_child(nodes, m_path[i]); | ||
848 | if(n == 0) | ||
610 | { | 849 | { |
611 | if(i == j) | 850 | path.clear(); |
612 | continue; | 851 | return path; |
613 | if(value[i].name == value[j].name) | ||
614 | err.push_back(prefix(make_fatal(value[i].name, | ||
615 | "there are several values with the same name"), at)); | ||
616 | if(value[i].value == value[j].value) | ||
617 | err.push_back(prefix(make_warning(value[i].name, | ||
618 | "there are several values with the same value"), at)); | ||
619 | } | 852 | } |
620 | if(value[i].value > (bitmask() >> first_bit)) | 853 | path.push_back(n->name); |
621 | err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name)); | 854 | nodes = &n->node; |
622 | if(recursive) | ||
623 | add_errors(err, value[i].errors(true), at); | ||
624 | } | 855 | } |
625 | return err; | 856 | return path; |
626 | } | 857 | } |
627 | 858 | ||
628 | std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive) | 859 | std::string node_ref_t::name() const |
629 | { | 860 | { |
630 | (void) recursive; | 861 | node_t *n = get(); |
631 | if(name.size() == 0) | 862 | return n == 0 ? "" : n->name; |
632 | return one_error(make_fatal("", "empty name")); | ||
633 | else if(!name_valid(name)) | ||
634 | return one_error(make_fatal(name, "invalid name")); | ||
635 | else | ||
636 | return no_error(); | ||
637 | } | 863 | } |
638 | 864 | ||
639 | std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive) | 865 | bool node_ref_t::operator==(const node_ref_t& ref) const |
640 | { | 866 | { |
641 | (void) recursive; | 867 | return m_soc == ref.m_soc && m_path == ref.m_path; |
642 | if(type == REG_FORMULA_STRING && string.size() == 0) | ||
643 | return one_error(make_fatal("", "empty string formula")); | ||
644 | else | ||
645 | return no_error(); | ||
646 | } | 868 | } |
647 | 869 | ||
648 | namespace | 870 | /** |
871 | * register_ref_t | ||
872 | */ | ||
873 | |||
874 | register_ref_t::register_ref_t(node_ref_t node) | ||
875 | :m_node(node) | ||
649 | { | 876 | { |
877 | } | ||
650 | 878 | ||
651 | bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b) | 879 | register_ref_t::register_ref_t() |
652 | { | 880 | { |
653 | return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit); | ||
654 | } | 881 | } |
655 | 882 | ||
883 | bool register_ref_t::valid() const | ||
884 | { | ||
885 | return get() != 0; | ||
656 | } | 886 | } |
657 | 887 | ||
658 | std::vector< soc_error_t > soc_reg_t::errors(bool recursive) | 888 | register_t *register_ref_t::get() const |
659 | { | 889 | { |
660 | std::vector< soc_error_t > err; | 890 | node_t *n = m_node.get(); |
661 | std::string at(name); | 891 | if(n == 0 || n->register_.empty()) |
662 | if(name.size() == 0) | 892 | return 0; |
663 | err.push_back(make_fatal(at, "empty name")); | 893 | return &n->register_[0]; |
664 | else if(!name_valid(name)) | ||
665 | err.push_back(make_fatal(at, "invalid name")); | ||
666 | for(size_t i = 0; i < addr.size(); i++) | ||
667 | { | ||
668 | for(size_t j = 0; j < addr.size(); j++) | ||
669 | { | ||
670 | if(i == j) | ||
671 | continue; | ||
672 | if(addr[i].name == addr[j].name) | ||
673 | err.push_back(prefix(make_fatal(addr[i].name, | ||
674 | "there are several instances with the same name"), at)); | ||
675 | if(addr[i].addr == addr[j].addr) | ||
676 | err.push_back(prefix(make_fatal(addr[i].name, | ||
677 | "there are several instances with the same address"), at)); | ||
678 | } | ||
679 | if(recursive) | ||
680 | add_errors(err, addr[i].errors(true), at); | ||
681 | } | ||
682 | if(recursive) | ||
683 | add_errors(err, formula.errors(true), at); | ||
684 | for(size_t i = 0; i < field.size(); i++) | ||
685 | { | ||
686 | for(size_t j = 0; j < field.size(); j++) | ||
687 | { | ||
688 | if(i == j) | ||
689 | continue; | ||
690 | if(field[i].name == field[j].name) | ||
691 | err.push_back(prefix(make_fatal(field[i].name, | ||
692 | "there are several fields with the same name"), at)); | ||
693 | if(field_overlap(field[i], field[j])) | ||
694 | err.push_back(prefix(make_fatal(field[i].name, | ||
695 | "there are overlapping fields"), at)); | ||
696 | } | ||
697 | if(recursive) | ||
698 | add_errors(err, field[i].errors(true), at); | ||
699 | } | ||
700 | return err; | ||
701 | } | 894 | } |
702 | 895 | ||
703 | std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive) | 896 | node_ref_t register_ref_t::node() const |
704 | { | 897 | { |
705 | (void) recursive; | 898 | return m_node; |
706 | if(name.size() == 0) | ||
707 | return one_error(make_fatal("", "empty name")); | ||
708 | else if(!name_valid(name)) | ||
709 | return one_error(make_fatal(name, "invalid name")); | ||
710 | else | ||
711 | return no_error(); | ||
712 | } | 899 | } |
713 | 900 | ||
714 | std::vector< soc_error_t > soc_dev_t::errors(bool recursive) | 901 | std::vector< field_ref_t > register_ref_t::fields() const |
715 | { | 902 | { |
716 | std::vector< soc_error_t > err; | 903 | std::vector< field_ref_t > fields; |
717 | std::string at(name); | 904 | register_t *r = get(); |
718 | if(name.size() == 0) | 905 | if(r == 0) |
719 | err.push_back(make_fatal(at, "empty name")); | 906 | return fields; |
720 | else if(!name_valid(name)) | 907 | for(size_t i = 0; i < r->field.size(); i++) |
721 | err.push_back(make_fatal(at, "invalid name")); | 908 | fields.push_back(field_ref_t(*this, r->field[i].id)); |
722 | for(size_t i = 0; i < addr.size(); i++) | 909 | return fields; |
723 | { | ||
724 | for(size_t j = 0; j < addr.size(); j++) | ||
725 | { | ||
726 | if(i == j) | ||
727 | continue; | ||
728 | if(addr[i].name == addr[j].name) | ||
729 | err.push_back(prefix(make_fatal(addr[i].name, | ||
730 | "there are several instances with the same name"), at)); | ||
731 | if(addr[i].addr == addr[j].addr) | ||
732 | err.push_back(prefix(make_fatal(addr[i].name, | ||
733 | "there are several instances with the same address"), at)); | ||
734 | } | ||
735 | if(recursive) | ||
736 | add_errors(err, addr[i].errors(true), at); | ||
737 | } | ||
738 | for(size_t i = 0; i < reg.size(); i++) | ||
739 | { | ||
740 | for(size_t j = 0; j < reg.size(); j++) | ||
741 | { | ||
742 | if(i == j) | ||
743 | continue; | ||
744 | if(reg[i].name == reg[j].name) | ||
745 | err.push_back(prefix(make_fatal(reg[i].name, | ||
746 | "there are several registers with the same name"), at)); | ||
747 | } | ||
748 | if(recursive) | ||
749 | add_errors(err, reg[i].errors(true), at); | ||
750 | } | ||
751 | return err; | ||
752 | } | 910 | } |
753 | 911 | ||
754 | std::vector< soc_error_t > soc_t::errors(bool recursive) | 912 | field_ref_t register_ref_t::field(const std::string& name) const |
755 | { | 913 | { |
756 | std::vector< soc_error_t > err; | 914 | register_t *r = get(); |
757 | std::string at(name); | 915 | if(r == 0) |
758 | for(size_t i = 0; i < dev.size(); i++) | 916 | return field_ref_t(); |
759 | { | 917 | for(size_t i = 0; i < r->field.size(); i++) |
760 | for(size_t j = 0; j < dev.size(); j++) | 918 | if(r->field[i].name == name) |
761 | { | 919 | return field_ref_t(*this, r->field[i].id); |
762 | if(i == j) | 920 | return field_ref_t(); |
763 | continue; | ||
764 | if(dev[i].name == dev[j].name) | ||
765 | err.push_back(prefix(make_fatal(dev[i].name, | ||
766 | "there are several devices with the same name"), at)); | ||
767 | } | ||
768 | if(recursive) | ||
769 | add_errors(err, dev[i].errors(true), at); | ||
770 | } | ||
771 | return err; | ||
772 | } | 921 | } |
773 | 922 | ||
774 | namespace | 923 | /** |
924 | * field_ref_t | ||
925 | */ | ||
926 | |||
927 | field_ref_t::field_ref_t(register_ref_t reg, soc_id_t id) | ||
928 | :m_reg(reg), m_id(id) | ||
775 | { | 929 | { |
930 | } | ||
776 | 931 | ||
777 | struct formula_evaluator | 932 | field_ref_t::field_ref_t() |
778 | { | 933 | { |
779 | std::string formula; | 934 | } |
780 | size_t pos; | ||
781 | std::string error; | ||
782 | 935 | ||
783 | bool err(const char *fmt, ...) | 936 | bool field_ref_t::valid() const |
784 | { | 937 | { |
785 | char buffer[256]; | 938 | return get() != 0; |
786 | va_list args; | 939 | } |
787 | va_start(args, fmt); | ||
788 | vsnprintf(buffer,sizeof(buffer), fmt, args); | ||
789 | va_end(args); | ||
790 | error = buffer; | ||
791 | return false; | ||
792 | } | ||
793 | 940 | ||
794 | formula_evaluator(const std::string& s):pos(0) | 941 | field_t *field_ref_t::get() const |
795 | { | 942 | { |
796 | for(size_t i = 0; i < s.size(); i++) | 943 | register_t *reg = m_reg.get(); |
797 | if(!isspace(s[i])) | 944 | if(reg == 0) |
798 | formula.push_back(s[i]); | 945 | return 0; |
799 | } | 946 | for(size_t i = 0; i < reg->field.size(); i++) |
947 | if(reg->field[i].id == m_id) | ||
948 | return ®->field[i]; | ||
949 | return 0; | ||
950 | } | ||
800 | 951 | ||
801 | void adv() | 952 | register_ref_t field_ref_t::reg() const |
802 | { | 953 | { |
803 | pos++; | 954 | return m_reg; |
804 | } | 955 | } |
805 | 956 | ||
806 | char cur() | 957 | /** |
807 | { | 958 | * node_inst_t |
808 | return end() ? 0 : formula[pos]; | 959 | */ |
809 | } | ||
810 | 960 | ||
811 | bool end() | 961 | namespace |
812 | { | 962 | { |
813 | return pos >= formula.size(); | 963 | |
814 | } | 964 | const size_t INST_NO_INDEX = std::numeric_limits<std::size_t>::max(); |
815 | 965 | ||
816 | bool parse_digit(char c, int basis, soc_word_t& res) | 966 | bool get_inst_addr(range_t& range, size_t index, soc_addr_t& addr) |
967 | { | ||
968 | if(index < range.first || index >= range.first + range.count) | ||
969 | return false; | ||
970 | switch(range.type) | ||
817 | { | 971 | { |
818 | c = tolower(c); | 972 | case range_t::STRIDE: |
819 | if(isdigit(c)) | 973 | addr += range.base + (index - range.first) * range.stride; |
820 | { | ||
821 | res = c - '0'; | ||
822 | return true; | 974 | return true; |
823 | } | 975 | case range_t::FORMULA: |
824 | if(basis == 16 && isxdigit(c)) | ||
825 | { | 976 | { |
826 | res = c + 10 - 'a'; | 977 | soc_word_t res; |
978 | std::map< std::string, soc_word_t > vars; | ||
979 | vars[range.variable] = index; | ||
980 | error_context_t ctx; | ||
981 | if(!evaluate_formula(range.formula, vars, res, "", ctx)) | ||
982 | return false; | ||
983 | addr += res; | ||
827 | return true; | 984 | return true; |
828 | } | 985 | } |
829 | return err("invalid digit '%c'", c); | 986 | default: |
987 | return false; | ||
830 | } | 988 | } |
989 | } | ||
831 | 990 | ||
832 | bool parse_signed(soc_word_t& res) | 991 | bool get_inst_addr(instance_t *inst, size_t index, soc_addr_t& addr) |
992 | { | ||
993 | if(inst == 0) | ||
994 | return false; | ||
995 | switch(inst->type) | ||
833 | { | 996 | { |
834 | char op = cur(); | 997 | case instance_t::SINGLE: |
835 | if(op == '+' || op == '-') | 998 | if(index != INST_NO_INDEX) |
836 | { | ||
837 | adv(); | ||
838 | if(!parse_signed(res)) | ||
839 | return false; | 999 | return false; |
840 | if(op == '-') | 1000 | addr += inst->addr; |
841 | res *= -1; | ||
842 | return true; | 1001 | return true; |
843 | } | 1002 | case instance_t::RANGE: |
844 | else if(op == '(') | 1003 | if(index == INST_NO_INDEX) |
845 | { | ||
846 | adv(); | ||
847 | if(!parse_expression(res)) | ||
848 | return false; | 1004 | return false; |
849 | if(cur() != ')') | 1005 | return get_inst_addr(inst->range, index, addr); |
850 | return err("expected ')', got '%c'", cur()); | 1006 | default: |
851 | adv(); | ||
852 | return true; | ||
853 | } | ||
854 | else if(isdigit(op)) | ||
855 | { | ||
856 | res = op - '0'; | ||
857 | adv(); | ||
858 | int basis = 10; | ||
859 | if(op == '0' && cur() == 'x') | ||
860 | { | ||
861 | basis = 16; | ||
862 | adv(); | ||
863 | } | ||
864 | soc_word_t digit = 0; | ||
865 | while(parse_digit(cur(), basis, digit)) | ||
866 | { | ||
867 | res = res * basis + digit; | ||
868 | adv(); | ||
869 | } | ||
870 | return true; | ||
871 | } | ||
872 | else if(isalpha(op) || op == '_') | ||
873 | { | ||
874 | std::string name; | ||
875 | while(isalnum(cur()) || cur() == '_') | ||
876 | { | ||
877 | name.push_back(cur()); | ||
878 | adv(); | ||
879 | } | ||
880 | return get_variable(name, res); | ||
881 | } | ||
882 | else | ||
883 | return err("express signed expression, got '%c'", op); | ||
884 | } | ||
885 | |||
886 | bool parse_term(soc_word_t& res) | ||
887 | { | ||
888 | if(!parse_signed(res)) | ||
889 | return false; | 1007 | return false; |
890 | while(cur() == '*' || cur() == '/' || cur() == '%') | ||
891 | { | ||
892 | char op = cur(); | ||
893 | adv(); | ||
894 | soc_word_t tmp; | ||
895 | if(!parse_signed(tmp)) | ||
896 | return false; | ||
897 | if(op == '*') | ||
898 | res *= tmp; | ||
899 | else if(tmp != 0) | ||
900 | res = op == '/' ? res / tmp : res % tmp; | ||
901 | else | ||
902 | return err("division by 0"); | ||
903 | } | ||
904 | return true; | ||
905 | } | 1008 | } |
1009 | } | ||
906 | 1010 | ||
907 | bool parse_expression(soc_word_t& res) | 1011 | } |
908 | { | ||
909 | if(!parse_term(res)) | ||
910 | return false; | ||
911 | while(!end() && (cur() == '+' || cur() == '-')) | ||
912 | { | ||
913 | char op = cur(); | ||
914 | adv(); | ||
915 | soc_word_t tmp; | ||
916 | if(!parse_term(tmp)) | ||
917 | return false; | ||
918 | if(op == '+') | ||
919 | res += tmp; | ||
920 | else | ||
921 | res -= tmp; | ||
922 | } | ||
923 | return true; | ||
924 | } | ||
925 | 1012 | ||
926 | bool parse(soc_word_t& res, std::string& _error) | 1013 | node_inst_t::node_inst_t(soc_ref_t soc) |
927 | { | 1014 | :m_node(soc.root()) |
928 | bool ok = parse_expression(res); | 1015 | { |
929 | if(ok && !end()) | 1016 | } |
930 | err("unexpected character '%c'", cur()); | ||
931 | _error = error; | ||
932 | return ok && end(); | ||
933 | } | ||
934 | 1017 | ||
935 | virtual bool get_variable(std::string name, soc_word_t& res) | 1018 | node_inst_t::node_inst_t(node_ref_t node, const std::vector< soc_id_t >& ids, |
936 | { | 1019 | const std::vector< size_t >& indexes) |
937 | return err("unknown variable '%s'", name.c_str()); | 1020 | :m_node(node), m_id_path(ids), m_index_path(indexes) |
938 | } | 1021 | { |
939 | }; | 1022 | } |
940 | 1023 | ||
941 | struct my_evaluator : public formula_evaluator | 1024 | node_inst_t::node_inst_t() |
942 | { | 1025 | { |
943 | const std::map< std::string, soc_word_t>& var; | 1026 | } |
944 | 1027 | ||
945 | my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var) | 1028 | bool node_inst_t::valid() const |
946 | :formula_evaluator(formula), var(_var) {} | 1029 | { |
1030 | return is_root() || get() != 0; | ||
1031 | } | ||
947 | 1032 | ||
948 | virtual bool get_variable(std::string name, soc_word_t& res) | 1033 | node_ref_t node_inst_t::node() const |
1034 | { | ||
1035 | return m_node; | ||
1036 | } | ||
1037 | |||
1038 | soc_ref_t node_inst_t::soc() const | ||
1039 | { | ||
1040 | return m_node.soc(); | ||
1041 | } | ||
1042 | |||
1043 | bool node_inst_t::is_root() const | ||
1044 | { | ||
1045 | return m_node.is_root(); | ||
1046 | } | ||
1047 | |||
1048 | node_inst_t node_inst_t::parent() const | ||
1049 | { | ||
1050 | std::vector< soc_id_t > ids = m_id_path; | ||
1051 | std::vector< size_t > indexes = m_index_path; | ||
1052 | if(!ids.empty()) | ||
1053 | ids.pop_back(); | ||
1054 | if(!indexes.empty()) | ||
1055 | indexes.pop_back(); | ||
1056 | return node_inst_t(m_node.parent(), ids, indexes); | ||
1057 | } | ||
1058 | |||
1059 | instance_t *node_inst_t::get() const | ||
1060 | { | ||
1061 | node_t *n = m_node.get(); | ||
1062 | if(n == 0) | ||
1063 | return 0; | ||
1064 | for(size_t i = 0; i < n->instance.size(); i++) | ||
1065 | if(n->instance[i].id == m_id_path.back()) | ||
1066 | return &n->instance[i]; | ||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | soc_addr_t node_inst_t::addr() const | ||
1071 | { | ||
1072 | if(is_root()) | ||
1073 | return 0; | ||
1074 | soc_addr_t addr = parent().addr(); | ||
1075 | if(!get_inst_addr(get(), m_index_path.back(), addr)) | ||
1076 | return 0; | ||
1077 | return addr; | ||
1078 | } | ||
1079 | |||
1080 | node_inst_t node_inst_t::child(const std::string& name) const | ||
1081 | { | ||
1082 | return child(name, INST_NO_INDEX); | ||
1083 | } | ||
1084 | |||
1085 | |||
1086 | node_inst_t node_inst_t::child(const std::string& name, size_t index) const | ||
1087 | { | ||
1088 | std::vector< node_t > *nodes = get_children(m_node); | ||
1089 | if(nodes == 0) | ||
1090 | return node_inst_t(); | ||
1091 | node_ref_t child_node = m_node; | ||
1092 | for(size_t i = 0; i < nodes->size(); i++) | ||
949 | { | 1093 | { |
950 | std::map< std::string, soc_word_t>::const_iterator it = var.find(name); | 1094 | node_t& node = (*nodes)[i]; |
951 | if(it == var.end()) | 1095 | child_node.m_path.push_back(node.id); |
952 | return formula_evaluator::get_variable(name, res); | 1096 | for(size_t j = 0; j < node.instance.size(); j++) |
953 | else | ||
954 | { | 1097 | { |
955 | res = it->second; | 1098 | if(node.instance[j].name != name) |
956 | return true; | 1099 | continue; |
1100 | std::vector< soc_id_t > ids = m_id_path; | ||
1101 | std::vector< size_t > indexes = m_index_path; | ||
1102 | ids.push_back(node.instance[j].id); | ||
1103 | ids.push_back(index); | ||
1104 | return node_inst_t(child_node, ids, indexes); | ||
957 | } | 1105 | } |
1106 | child_node.m_path.pop_back(); | ||
958 | } | 1107 | } |
959 | }; | 1108 | return node_inst_t(); |
1109 | } | ||
1110 | |||
1111 | std::vector< node_inst_t > node_inst_t::children() const | ||
1112 | { | ||
1113 | std::vector< node_inst_t > list; | ||
1114 | std::vector< node_t > *nodes = get_children(m_node); | ||
1115 | std::vector< soc_id_t > n_path = m_id_path; | ||
1116 | std::vector< size_t > i_path = m_index_path; | ||
1117 | if(nodes == 0) | ||
1118 | return list; | ||
1119 | node_ref_t child_node = m_node; | ||
1120 | for(size_t i = 0; i < nodes->size(); i++) | ||
1121 | { | ||
1122 | node_t& node = (*nodes)[i]; | ||
1123 | child_node.m_path.push_back(node.id); | ||
1124 | for(size_t j = 0; j < node.instance.size(); j++) | ||
1125 | { | ||
1126 | instance_t& inst = node.instance[j]; | ||
1127 | n_path.push_back(inst.id); | ||
1128 | switch(inst.type) | ||
1129 | { | ||
1130 | case instance_t::SINGLE: | ||
1131 | i_path.push_back(INST_NO_INDEX); | ||
1132 | list.push_back(node_inst_t(child_node, n_path, i_path)); | ||
1133 | i_path.pop_back(); | ||
1134 | break; | ||
1135 | case instance_t::RANGE: | ||
1136 | for(size_t i = 0; i < inst.range.count; i++) | ||
1137 | { | ||
1138 | i_path.push_back(inst.range.first + i); | ||
1139 | list.push_back(node_inst_t(child_node, n_path, i_path)); | ||
1140 | i_path.pop_back(); | ||
1141 | } | ||
1142 | break; | ||
1143 | default: | ||
1144 | break; | ||
1145 | } | ||
1146 | n_path.pop_back(); | ||
1147 | } | ||
1148 | child_node.m_path.pop_back(); | ||
1149 | } | ||
1150 | return list; | ||
1151 | } | ||
1152 | |||
1153 | std::string node_inst_t::name() const | ||
1154 | { | ||
1155 | instance_t *inst = get(); | ||
1156 | return inst == 0 ? "" : inst->name; | ||
1157 | } | ||
960 | 1158 | ||
1159 | bool node_inst_t:: is_indexed() const | ||
1160 | { | ||
1161 | return !m_index_path.empty() && m_index_path.back() != INST_NO_INDEX; | ||
961 | } | 1162 | } |
962 | 1163 | ||
963 | bool soc_desc_evaluate_formula(const std::string& formula, | 1164 | size_t node_inst_t::index() const |
964 | const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error) | ||
965 | { | 1165 | { |
966 | my_evaluator e(formula, var); | 1166 | return m_index_path.empty() ? INST_NO_INDEX : m_index_path.back(); |
967 | return e.parse(result, error); | ||
968 | } | 1167 | } |
969 | 1168 | ||
970 | /** WARNING we need to call xmlInitParser() to init libxml2 but it needs to | 1169 | /** WARNING we need to call xmlInitParser() to init libxml2 but it needs to |
@@ -982,4 +1181,7 @@ public: | |||
982 | }; | 1181 | }; |
983 | 1182 | ||
984 | xml_parser_init __xml_parser_init; | 1183 | xml_parser_init __xml_parser_init; |
985 | } \ No newline at end of file | 1184 | } |
1185 | |||
1186 | } // soc_desc_v1 | ||
1187 | |||
diff --git a/utils/regtools/lib/soc_desc_v1.cpp b/utils/regtools/lib/soc_desc_v1.cpp new file mode 100644 index 0000000000..d585485493 --- /dev/null +++ b/utils/regtools/lib/soc_desc_v1.cpp | |||
@@ -0,0 +1,990 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2012 by Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "soc_desc_v1.hpp" | ||
22 | #include <libxml/parser.h> | ||
23 | #include <libxml/tree.h> | ||
24 | #include <libxml/xmlsave.h> | ||
25 | #include <libxml/xmlwriter.h> | ||
26 | #include <stdio.h> | ||
27 | #include <string.h> | ||
28 | #include <algorithm> | ||
29 | #include <cctype> | ||
30 | |||
31 | namespace soc_desc_v1 | ||
32 | { | ||
33 | |||
34 | #define XML_CHAR_TO_CHAR(s) ((const char *)(s)) | ||
35 | |||
36 | #define BEGIN_ATTR_MATCH(attr) \ | ||
37 | for(xmlAttr *a = attr; a; a = a->next) { | ||
38 | |||
39 | #define MATCH_X_ATTR(attr_name, hook, ...) \ | ||
40 | if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ | ||
41 | std::string s; \ | ||
42 | if(!parse_text_attr(a, s) || !hook(s, __VA_ARGS__)) \ | ||
43 | return false; \ | ||
44 | } | ||
45 | |||
46 | #define SOFT_MATCH_X_ATTR(attr_name, hook, ...) \ | ||
47 | if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ | ||
48 | std::string s; \ | ||
49 | if(parse_text_attr(a, s)) \ | ||
50 | hook(s, __VA_ARGS__); \ | ||
51 | } | ||
52 | |||
53 | #define SOFT_MATCH_SCT_ATTR(attr_name, var) \ | ||
54 | SOFT_MATCH_X_ATTR(attr_name, validate_sct_hook, var) | ||
55 | |||
56 | #define MATCH_TEXT_ATTR(attr_name, var) \ | ||
57 | MATCH_X_ATTR(attr_name, validate_string_hook, var) | ||
58 | |||
59 | #define MATCH_UINT32_ATTR(attr_name, var) \ | ||
60 | MATCH_X_ATTR(attr_name, validate_uint32_hook, var) | ||
61 | |||
62 | #define MATCH_BITRANGE_ATTR(attr_name, first, last) \ | ||
63 | MATCH_X_ATTR(attr_name, validate_bitrange_hook, first, last) | ||
64 | |||
65 | #define END_ATTR_MATCH() \ | ||
66 | } | ||
67 | |||
68 | #define BEGIN_NODE_MATCH(node) \ | ||
69 | for(xmlNode *sub = node; sub; sub = sub->next) { | ||
70 | |||
71 | #define MATCH_ELEM_NODE(node_name, array, parse_fn) \ | ||
72 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ | ||
73 | array.resize(array.size() + 1); \ | ||
74 | if(!parse_fn(sub, array.back())) \ | ||
75 | return false; \ | ||
76 | } | ||
77 | |||
78 | #define SOFT_MATCH_ELEM_NODE(node_name, array, parse_fn) \ | ||
79 | if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ | ||
80 | array.resize(array.size() + 1); \ | ||
81 | if(!parse_fn(sub, array.back())) \ | ||
82 | array.pop_back(); \ | ||
83 | } | ||
84 | |||
85 | #define END_NODE_MATCH() \ | ||
86 | } | ||
87 | |||
88 | namespace | ||
89 | { | ||
90 | |||
91 | bool validate_string_hook(const std::string& str, std::string& s) | ||
92 | { | ||
93 | s = str; | ||
94 | return true; | ||
95 | } | ||
96 | |||
97 | bool validate_sct_hook(const std::string& str, soc_reg_flags_t& flags) | ||
98 | { | ||
99 | if(str == "yes") flags |= REG_HAS_SCT; | ||
100 | else if(str != "no") return false; | ||
101 | return true; | ||
102 | } | ||
103 | |||
104 | bool validate_unsigned_long_hook(const std::string& str, unsigned long& s) | ||
105 | { | ||
106 | char *end; | ||
107 | s = strtoul(str.c_str(), &end, 0); | ||
108 | return *end == 0; | ||
109 | } | ||
110 | |||
111 | bool validate_uint32_hook(const std::string& str, uint32_t& s) | ||
112 | { | ||
113 | unsigned long u; | ||
114 | if(!validate_unsigned_long_hook(str, u)) return false; | ||
115 | #if ULONG_MAX > 0xffffffff | ||
116 | if(u > 0xffffffff) return false; | ||
117 | #endif | ||
118 | s = u; | ||
119 | return true; | ||
120 | } | ||
121 | |||
122 | bool validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last) | ||
123 | { | ||
124 | unsigned long a, b; | ||
125 | size_t sep = str.find(':'); | ||
126 | if(sep == std::string::npos) return false; | ||
127 | if(!validate_unsigned_long_hook(str.substr(0, sep), a)) return false; | ||
128 | if(!validate_unsigned_long_hook(str.substr(sep + 1), b)) return false; | ||
129 | if(a > 31 || b > 31 || a < b) return false; | ||
130 | first = b; | ||
131 | last = a; | ||
132 | return true; | ||
133 | } | ||
134 | |||
135 | bool parse_text_attr(xmlAttr *attr, std::string& s) | ||
136 | { | ||
137 | if(attr->children != attr->last) | ||
138 | return false; | ||
139 | if(attr->children->type != XML_TEXT_NODE) | ||
140 | return false; | ||
141 | s = XML_CHAR_TO_CHAR(attr->children->content); | ||
142 | return true; | ||
143 | } | ||
144 | |||
145 | bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value) | ||
146 | { | ||
147 | BEGIN_ATTR_MATCH(node->properties) | ||
148 | MATCH_TEXT_ATTR("name", value.name) | ||
149 | MATCH_UINT32_ATTR("value", value.value) | ||
150 | MATCH_TEXT_ATTR("desc", value.desc) | ||
151 | END_ATTR_MATCH() | ||
152 | |||
153 | return true; | ||
154 | } | ||
155 | |||
156 | bool parse_field_elem(xmlNode *node, soc_reg_field_t& field) | ||
157 | { | ||
158 | BEGIN_ATTR_MATCH(node->properties) | ||
159 | MATCH_TEXT_ATTR("name", field.name) | ||
160 | MATCH_BITRANGE_ATTR("bitrange", field.first_bit, field.last_bit) | ||
161 | MATCH_TEXT_ATTR("desc", field.desc) | ||
162 | END_ATTR_MATCH() | ||
163 | |||
164 | BEGIN_NODE_MATCH(node->children) | ||
165 | SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem) | ||
166 | END_NODE_MATCH() | ||
167 | |||
168 | return true; | ||
169 | } | ||
170 | |||
171 | bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr) | ||
172 | { | ||
173 | BEGIN_ATTR_MATCH(node->properties) | ||
174 | MATCH_TEXT_ATTR("name", addr.name) | ||
175 | MATCH_UINT32_ATTR("addr", addr.addr) | ||
176 | END_ATTR_MATCH() | ||
177 | |||
178 | return true; | ||
179 | } | ||
180 | |||
181 | bool parse_reg_formula_elem(xmlNode *node, soc_reg_formula_t& formula) | ||
182 | { | ||
183 | BEGIN_ATTR_MATCH(node->properties) | ||
184 | MATCH_TEXT_ATTR("string", formula.string) | ||
185 | END_ATTR_MATCH() | ||
186 | |||
187 | formula.type = REG_FORMULA_STRING; | ||
188 | |||
189 | return true; | ||
190 | } | ||
191 | |||
192 | bool parse_add_trivial_addr(const std::string& str, soc_reg_t& reg) | ||
193 | { | ||
194 | soc_reg_addr_t a; | ||
195 | a.name = reg.name; | ||
196 | if(!validate_uint32_hook(str, a.addr)) | ||
197 | return false; | ||
198 | reg.addr.push_back(a); | ||
199 | return true; | ||
200 | } | ||
201 | |||
202 | bool parse_reg_elem(xmlNode *node, soc_reg_t& reg) | ||
203 | { | ||
204 | std::list< soc_reg_formula_t > formulas; | ||
205 | BEGIN_ATTR_MATCH(node->properties) | ||
206 | MATCH_TEXT_ATTR("name", reg.name) | ||
207 | SOFT_MATCH_SCT_ATTR("sct", reg.flags) | ||
208 | SOFT_MATCH_X_ATTR("addr", parse_add_trivial_addr, reg) | ||
209 | MATCH_TEXT_ATTR("desc", reg.desc) | ||
210 | END_ATTR_MATCH() | ||
211 | |||
212 | BEGIN_NODE_MATCH(node->children) | ||
213 | MATCH_ELEM_NODE("addr", reg.addr, parse_reg_addr_elem) | ||
214 | MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem) | ||
215 | MATCH_ELEM_NODE("field", reg.field, parse_field_elem) | ||
216 | END_NODE_MATCH() | ||
217 | |||
218 | if(formulas.size() > 1) | ||
219 | { | ||
220 | fprintf(stderr, "Only one formula is allowed per register\n"); | ||
221 | return false; | ||
222 | } | ||
223 | if(formulas.size() == 1) | ||
224 | reg.formula = formulas.front(); | ||
225 | |||
226 | return true; | ||
227 | } | ||
228 | |||
229 | bool parse_dev_addr_elem(xmlNode *node, soc_dev_addr_t& addr) | ||
230 | { | ||
231 | BEGIN_ATTR_MATCH(node->properties) | ||
232 | MATCH_TEXT_ATTR("name", addr.name) | ||
233 | MATCH_UINT32_ATTR("addr", addr.addr) | ||
234 | END_ATTR_MATCH() | ||
235 | |||
236 | return true; | ||
237 | } | ||
238 | |||
239 | bool parse_dev_elem(xmlNode *node, soc_dev_t& dev) | ||
240 | { | ||
241 | BEGIN_ATTR_MATCH(node->properties) | ||
242 | MATCH_TEXT_ATTR("name", dev.name) | ||
243 | MATCH_TEXT_ATTR("long_name", dev.long_name) | ||
244 | MATCH_TEXT_ATTR("desc", dev.desc) | ||
245 | MATCH_TEXT_ATTR("version", dev.version) | ||
246 | END_ATTR_MATCH() | ||
247 | |||
248 | BEGIN_NODE_MATCH(node->children) | ||
249 | MATCH_ELEM_NODE("addr", dev.addr, parse_dev_addr_elem) | ||
250 | MATCH_ELEM_NODE("reg", dev.reg, parse_reg_elem) | ||
251 | END_NODE_MATCH() | ||
252 | |||
253 | return true; | ||
254 | } | ||
255 | |||
256 | bool parse_soc_elem(xmlNode *node, soc_t& soc) | ||
257 | { | ||
258 | BEGIN_ATTR_MATCH(node->properties) | ||
259 | MATCH_TEXT_ATTR("name", soc.name) | ||
260 | MATCH_TEXT_ATTR("desc", soc.desc) | ||
261 | END_ATTR_MATCH() | ||
262 | |||
263 | BEGIN_NODE_MATCH(node->children) | ||
264 | MATCH_ELEM_NODE("dev", soc.dev, parse_dev_elem) | ||
265 | END_NODE_MATCH() | ||
266 | |||
267 | return true; | ||
268 | } | ||
269 | |||
270 | bool parse_root_elem(xmlNode *node, soc_t& soc) | ||
271 | { | ||
272 | std::vector< soc_t > socs; | ||
273 | BEGIN_NODE_MATCH(node) | ||
274 | MATCH_ELEM_NODE("soc", socs, parse_soc_elem) | ||
275 | END_NODE_MATCH() | ||
276 | if(socs.size() != 1) | ||
277 | { | ||
278 | fprintf(stderr, "A description file must contain exactly one soc element\n"); | ||
279 | return false; | ||
280 | } | ||
281 | soc = socs[0]; | ||
282 | return true; | ||
283 | } | ||
284 | |||
285 | } | ||
286 | |||
287 | bool parse_xml(const std::string& filename, soc_t& socs) | ||
288 | { | ||
289 | LIBXML_TEST_VERSION | ||
290 | |||
291 | xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0); | ||
292 | if(doc == NULL) | ||
293 | return false; | ||
294 | |||
295 | xmlNodePtr root_element = xmlDocGetRootElement(doc); | ||
296 | bool ret = parse_root_elem(root_element, socs); | ||
297 | |||
298 | xmlFreeDoc(doc); | ||
299 | |||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | namespace | ||
304 | { | ||
305 | |||
306 | int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field) | ||
307 | { | ||
308 | #define SAFE(x) if((x) < 0) return -1; | ||
309 | /* <field> */ | ||
310 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); | ||
311 | /* name */ | ||
312 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); | ||
313 | /* desc */ | ||
314 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); | ||
315 | /* bitrange */ | ||
316 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d", | ||
317 | field.last_bit, field.first_bit)); | ||
318 | /* values */ | ||
319 | for(size_t i = 0; i < field.value.size(); i++) | ||
320 | { | ||
321 | /* <value> */ | ||
322 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value")); | ||
323 | /* name */ | ||
324 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str())); | ||
325 | /* value */ | ||
326 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value)); | ||
327 | /* name */ | ||
328 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str())); | ||
329 | /* </value> */ | ||
330 | SAFE(xmlTextWriterEndElement(writer)); | ||
331 | } | ||
332 | /* </field> */ | ||
333 | SAFE(xmlTextWriterEndElement(writer)); | ||
334 | #undef SAFE | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg) | ||
339 | { | ||
340 | #define SAFE(x) if((x) < 0) return -1; | ||
341 | /* <reg> */ | ||
342 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg")); | ||
343 | /* name */ | ||
344 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str())); | ||
345 | /* name */ | ||
346 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str())); | ||
347 | /* flags */ | ||
348 | if(reg.flags & REG_HAS_SCT) | ||
349 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes")); | ||
350 | /* formula */ | ||
351 | if(reg.formula.type != REG_FORMULA_NONE) | ||
352 | { | ||
353 | /* <formula> */ | ||
354 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula")); | ||
355 | switch(reg.formula.type) | ||
356 | { | ||
357 | case REG_FORMULA_STRING: | ||
358 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string", | ||
359 | BAD_CAST reg.formula.string.c_str())); | ||
360 | break; | ||
361 | default: | ||
362 | break; | ||
363 | } | ||
364 | /* </formula> */ | ||
365 | SAFE(xmlTextWriterEndElement(writer)); | ||
366 | } | ||
367 | /* addresses */ | ||
368 | for(size_t i = 0; i < reg.addr.size(); i++) | ||
369 | { | ||
370 | /* <addr> */ | ||
371 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); | ||
372 | /* name */ | ||
373 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str())); | ||
374 | /* addr */ | ||
375 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr)); | ||
376 | /* </addr> */ | ||
377 | SAFE(xmlTextWriterEndElement(writer)); | ||
378 | } | ||
379 | /* fields */ | ||
380 | for(size_t i = 0; i < reg.field.size(); i++) | ||
381 | produce_field(writer, reg.field[i]); | ||
382 | /* </reg> */ | ||
383 | SAFE(xmlTextWriterEndElement(writer)); | ||
384 | #undef SAFE | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev) | ||
389 | { | ||
390 | #define SAFE(x) if((x) < 0) return -1; | ||
391 | /* <dev> */ | ||
392 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev")); | ||
393 | /* name */ | ||
394 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str())); | ||
395 | /* long_name */ | ||
396 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str())); | ||
397 | /* desc */ | ||
398 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str())); | ||
399 | /* version */ | ||
400 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str())); | ||
401 | /* addresses */ | ||
402 | for(size_t i = 0; i < dev.addr.size(); i++) | ||
403 | { | ||
404 | /* <addr> */ | ||
405 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); | ||
406 | /* name */ | ||
407 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str())); | ||
408 | /* addr */ | ||
409 | SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr)); | ||
410 | /* </addr> */ | ||
411 | SAFE(xmlTextWriterEndElement(writer)); | ||
412 | } | ||
413 | /* registers */ | ||
414 | for(size_t i = 0; i < dev.reg.size(); i++) | ||
415 | produce_reg(writer, dev.reg[i]); | ||
416 | /* </dev> */ | ||
417 | SAFE(xmlTextWriterEndElement(writer)); | ||
418 | #undef SAFE | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | } | ||
423 | |||
424 | bool produce_xml(const std::string& filename, const soc_t& soc) | ||
425 | { | ||
426 | LIBXML_TEST_VERSION | ||
427 | |||
428 | xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0); | ||
429 | if(writer == NULL) | ||
430 | return false; | ||
431 | #define SAFE(x) if((x) < 0) goto Lerr | ||
432 | SAFE(xmlTextWriterSetIndent(writer, 1)); | ||
433 | SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " ")); | ||
434 | /* <xml> */ | ||
435 | SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL)); | ||
436 | /* <soc> */ | ||
437 | SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc")); | ||
438 | /* name */ | ||
439 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str())); | ||
440 | /* desc */ | ||
441 | SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str())); | ||
442 | /* devices */ | ||
443 | for(size_t i = 0; i < soc.dev.size(); i++) | ||
444 | SAFE(produce_dev(writer, soc.dev[i])); | ||
445 | /* end <soc> */ | ||
446 | SAFE(xmlTextWriterEndElement(writer)); | ||
447 | /* </xml> */ | ||
448 | SAFE(xmlTextWriterEndDocument(writer)); | ||
449 | xmlFreeTextWriter(writer); | ||
450 | return true; | ||
451 | #undef SAFE | ||
452 | Lerr: | ||
453 | xmlFreeTextWriter(writer); | ||
454 | return false; | ||
455 | } | ||
456 | |||
457 | namespace | ||
458 | { | ||
459 | |||
460 | struct soc_sorter | ||
461 | { | ||
462 | bool operator()(const soc_dev_t& a, const soc_dev_t& b) const | ||
463 | { | ||
464 | return a.name < b.name; | ||
465 | } | ||
466 | |||
467 | bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const | ||
468 | { | ||
469 | return a.name < b.name; | ||
470 | } | ||
471 | |||
472 | bool operator()(const soc_reg_t& a, const soc_reg_t& b) const | ||
473 | { | ||
474 | soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0; | ||
475 | soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0; | ||
476 | return aa < ab; | ||
477 | } | ||
478 | |||
479 | bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const | ||
480 | { | ||
481 | return a.addr < b.addr; | ||
482 | } | ||
483 | |||
484 | bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const | ||
485 | { | ||
486 | return a.last_bit > b.last_bit; | ||
487 | } | ||
488 | |||
489 | bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const | ||
490 | { | ||
491 | return a.value < b.value; | ||
492 | } | ||
493 | }; | ||
494 | |||
495 | void normalize(soc_reg_field_t& field) | ||
496 | { | ||
497 | std::sort(field.value.begin(), field.value.end(), soc_sorter()); | ||
498 | } | ||
499 | |||
500 | void normalize(soc_reg_t& reg) | ||
501 | { | ||
502 | std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter()); | ||
503 | std::sort(reg.field.begin(), reg.field.end(), soc_sorter()); | ||
504 | for(size_t i = 0; i < reg.field.size(); i++) | ||
505 | normalize(reg.field[i]); | ||
506 | } | ||
507 | |||
508 | void normalize(soc_dev_t& dev) | ||
509 | { | ||
510 | std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter()); | ||
511 | std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter()); | ||
512 | for(size_t i = 0; i < dev.reg.size(); i++) | ||
513 | normalize(dev.reg[i]); | ||
514 | } | ||
515 | |||
516 | } | ||
517 | |||
518 | void normalize(soc_t& soc) | ||
519 | { | ||
520 | std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter()); | ||
521 | for(size_t i = 0; i < soc.dev.size(); i++) | ||
522 | normalize(soc.dev[i]); | ||
523 | } | ||
524 | |||
525 | namespace | ||
526 | { | ||
527 | soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what) | ||
528 | { | ||
529 | soc_error_t err; | ||
530 | err.level = lvl; | ||
531 | err.location = at; | ||
532 | err.message = what; | ||
533 | return err; | ||
534 | } | ||
535 | |||
536 | soc_error_t make_warning(std::string at, std::string what) | ||
537 | { | ||
538 | return make_error(SOC_ERROR_WARNING, at, what); | ||
539 | } | ||
540 | |||
541 | soc_error_t make_fatal(std::string at, std::string what) | ||
542 | { | ||
543 | return make_error(SOC_ERROR_FATAL, at, what); | ||
544 | } | ||
545 | |||
546 | soc_error_t prefix(soc_error_t err, const std::string& prefix_at) | ||
547 | { | ||
548 | err.location = prefix_at + "." + err.location; | ||
549 | return err; | ||
550 | } | ||
551 | |||
552 | void add_errors(std::vector< soc_error_t >& errors, | ||
553 | const std::vector< soc_error_t >& new_errors, const std::string& prefix_at) | ||
554 | { | ||
555 | for(size_t i = 0; i < new_errors.size(); i++) | ||
556 | errors.push_back(prefix(new_errors[i], prefix_at)); | ||
557 | } | ||
558 | |||
559 | std::vector< soc_error_t > no_error() | ||
560 | { | ||
561 | std::vector< soc_error_t > s; | ||
562 | return s; | ||
563 | } | ||
564 | |||
565 | std::vector< soc_error_t > one_error(const soc_error_t& err) | ||
566 | { | ||
567 | std::vector< soc_error_t > s; | ||
568 | s.push_back(err); | ||
569 | return s; | ||
570 | } | ||
571 | |||
572 | bool name_valid(char c) | ||
573 | { | ||
574 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || | ||
575 | (c >= 'A' && c <= 'Z') || c == '_'; | ||
576 | } | ||
577 | |||
578 | bool name_valid(const std::string& s) | ||
579 | { | ||
580 | for(size_t i = 0; i < s.size(); i++) | ||
581 | if(!name_valid(s[i])) | ||
582 | return false; | ||
583 | return true; | ||
584 | } | ||
585 | } | ||
586 | |||
587 | std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive) | ||
588 | { | ||
589 | (void) recursive; | ||
590 | if(name.size() == 0) | ||
591 | return one_error(make_fatal(name, "empty name")); | ||
592 | else if(!name_valid(name)) | ||
593 | return one_error(make_fatal(name, "invalid name")); | ||
594 | else | ||
595 | return no_error(); | ||
596 | } | ||
597 | |||
598 | std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive) | ||
599 | { | ||
600 | std::vector< soc_error_t > err; | ||
601 | std::string at(name); | ||
602 | if(name.size() == 0) | ||
603 | err.push_back(make_fatal(at, "empty name")); | ||
604 | else if(!name_valid(name)) | ||
605 | err.push_back(make_fatal(at, "invalid name")); | ||
606 | if(last_bit > 31) | ||
607 | err.push_back(make_fatal(at, "last bit is greater than 31")); | ||
608 | if(first_bit > last_bit) | ||
609 | err.push_back(make_fatal(at, "last bit is greater than first bit")); | ||
610 | for(size_t i = 0; i < value.size(); i++) | ||
611 | { | ||
612 | for(size_t j = 0; j < value.size(); j++) | ||
613 | { | ||
614 | if(i == j) | ||
615 | continue; | ||
616 | if(value[i].name == value[j].name) | ||
617 | err.push_back(prefix(make_fatal(value[i].name, | ||
618 | "there are several values with the same name"), at)); | ||
619 | if(value[i].value == value[j].value) | ||
620 | err.push_back(prefix(make_warning(value[i].name, | ||
621 | "there are several values with the same value"), at)); | ||
622 | } | ||
623 | if(value[i].value > (bitmask() >> first_bit)) | ||
624 | err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name)); | ||
625 | if(recursive) | ||
626 | add_errors(err, value[i].errors(true), at); | ||
627 | } | ||
628 | return err; | ||
629 | } | ||
630 | |||
631 | std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive) | ||
632 | { | ||
633 | (void) recursive; | ||
634 | if(name.size() == 0) | ||
635 | return one_error(make_fatal("", "empty name")); | ||
636 | else if(!name_valid(name)) | ||
637 | return one_error(make_fatal(name, "invalid name")); | ||
638 | else | ||
639 | return no_error(); | ||
640 | } | ||
641 | |||
642 | std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive) | ||
643 | { | ||
644 | (void) recursive; | ||
645 | if(type == REG_FORMULA_STRING && string.size() == 0) | ||
646 | return one_error(make_fatal("", "empty string formula")); | ||
647 | else | ||
648 | return no_error(); | ||
649 | } | ||
650 | |||
651 | namespace | ||
652 | { | ||
653 | |||
654 | bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b) | ||
655 | { | ||
656 | return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit); | ||
657 | } | ||
658 | |||
659 | } | ||
660 | |||
661 | std::vector< soc_error_t > soc_reg_t::errors(bool recursive) | ||
662 | { | ||
663 | std::vector< soc_error_t > err; | ||
664 | std::string at(name); | ||
665 | if(name.size() == 0) | ||
666 | err.push_back(make_fatal(at, "empty name")); | ||
667 | else if(!name_valid(name)) | ||
668 | err.push_back(make_fatal(at, "invalid name")); | ||
669 | for(size_t i = 0; i < addr.size(); i++) | ||
670 | { | ||
671 | for(size_t j = 0; j < addr.size(); j++) | ||
672 | { | ||
673 | if(i == j) | ||
674 | continue; | ||
675 | if(addr[i].name == addr[j].name) | ||
676 | err.push_back(prefix(make_fatal(addr[i].name, | ||
677 | "there are several instances with the same name"), at)); | ||
678 | if(addr[i].addr == addr[j].addr) | ||
679 | err.push_back(prefix(make_fatal(addr[i].name, | ||
680 | "there are several instances with the same address"), at)); | ||
681 | } | ||
682 | if(recursive) | ||
683 | add_errors(err, addr[i].errors(true), at); | ||
684 | } | ||
685 | if(recursive) | ||
686 | add_errors(err, formula.errors(true), at); | ||
687 | for(size_t i = 0; i < field.size(); i++) | ||
688 | { | ||
689 | for(size_t j = 0; j < field.size(); j++) | ||
690 | { | ||
691 | if(i == j) | ||
692 | continue; | ||
693 | if(field[i].name == field[j].name) | ||
694 | err.push_back(prefix(make_fatal(field[i].name, | ||
695 | "there are several fields with the same name"), at)); | ||
696 | if(field_overlap(field[i], field[j])) | ||
697 | err.push_back(prefix(make_fatal(field[i].name, | ||
698 | "there are overlapping fields"), at)); | ||
699 | } | ||
700 | if(recursive) | ||
701 | add_errors(err, field[i].errors(true), at); | ||
702 | } | ||
703 | return err; | ||
704 | } | ||
705 | |||
706 | std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive) | ||
707 | { | ||
708 | (void) recursive; | ||
709 | if(name.size() == 0) | ||
710 | return one_error(make_fatal("", "empty name")); | ||
711 | else if(!name_valid(name)) | ||
712 | return one_error(make_fatal(name, "invalid name")); | ||
713 | else | ||
714 | return no_error(); | ||
715 | } | ||
716 | |||
717 | std::vector< soc_error_t > soc_dev_t::errors(bool recursive) | ||
718 | { | ||
719 | std::vector< soc_error_t > err; | ||
720 | std::string at(name); | ||
721 | if(name.size() == 0) | ||
722 | err.push_back(make_fatal(at, "empty name")); | ||
723 | else if(!name_valid(name)) | ||
724 | err.push_back(make_fatal(at, "invalid name")); | ||
725 | for(size_t i = 0; i < addr.size(); i++) | ||
726 | { | ||
727 | for(size_t j = 0; j < addr.size(); j++) | ||
728 | { | ||
729 | if(i == j) | ||
730 | continue; | ||
731 | if(addr[i].name == addr[j].name) | ||
732 | err.push_back(prefix(make_fatal(addr[i].name, | ||
733 | "there are several instances with the same name"), at)); | ||
734 | if(addr[i].addr == addr[j].addr) | ||
735 | err.push_back(prefix(make_fatal(addr[i].name, | ||
736 | "there are several instances with the same address"), at)); | ||
737 | } | ||
738 | if(recursive) | ||
739 | add_errors(err, addr[i].errors(true), at); | ||
740 | } | ||
741 | for(size_t i = 0; i < reg.size(); i++) | ||
742 | { | ||
743 | for(size_t j = 0; j < reg.size(); j++) | ||
744 | { | ||
745 | if(i == j) | ||
746 | continue; | ||
747 | if(reg[i].name == reg[j].name) | ||
748 | err.push_back(prefix(make_fatal(reg[i].name, | ||
749 | "there are several registers with the same name"), at)); | ||
750 | } | ||
751 | if(recursive) | ||
752 | add_errors(err, reg[i].errors(true), at); | ||
753 | } | ||
754 | return err; | ||
755 | } | ||
756 | |||
757 | std::vector< soc_error_t > soc_t::errors(bool recursive) | ||
758 | { | ||
759 | std::vector< soc_error_t > err; | ||
760 | std::string at(name); | ||
761 | for(size_t i = 0; i < dev.size(); i++) | ||
762 | { | ||
763 | for(size_t j = 0; j < dev.size(); j++) | ||
764 | { | ||
765 | if(i == j) | ||
766 | continue; | ||
767 | if(dev[i].name == dev[j].name) | ||
768 | err.push_back(prefix(make_fatal(dev[i].name, | ||
769 | "there are several devices with the same name"), at)); | ||
770 | } | ||
771 | if(recursive) | ||
772 | add_errors(err, dev[i].errors(true), at); | ||
773 | } | ||
774 | return err; | ||
775 | } | ||
776 | |||
777 | namespace | ||
778 | { | ||
779 | |||
780 | struct formula_evaluator | ||
781 | { | ||
782 | std::string formula; | ||
783 | size_t pos; | ||
784 | std::string error; | ||
785 | |||
786 | bool err(const char *fmt, ...) | ||
787 | { | ||
788 | char buffer[256]; | ||
789 | va_list args; | ||
790 | va_start(args, fmt); | ||
791 | vsnprintf(buffer,sizeof(buffer), fmt, args); | ||
792 | va_end(args); | ||
793 | error = buffer; | ||
794 | return false; | ||
795 | } | ||
796 | |||
797 | formula_evaluator(const std::string& s):pos(0) | ||
798 | { | ||
799 | for(size_t i = 0; i < s.size(); i++) | ||
800 | if(!isspace(s[i])) | ||
801 | formula.push_back(s[i]); | ||
802 | } | ||
803 | |||
804 | void adv() | ||
805 | { | ||
806 | pos++; | ||
807 | } | ||
808 | |||
809 | char cur() | ||
810 | { | ||
811 | return end() ? 0 : formula[pos]; | ||
812 | } | ||
813 | |||
814 | bool end() | ||
815 | { | ||
816 | return pos >= formula.size(); | ||
817 | } | ||
818 | |||
819 | bool parse_digit(char c, int basis, soc_word_t& res) | ||
820 | { | ||
821 | c = tolower(c); | ||
822 | if(isdigit(c)) | ||
823 | { | ||
824 | res = c - '0'; | ||
825 | return true; | ||
826 | } | ||
827 | if(basis == 16 && isxdigit(c)) | ||
828 | { | ||
829 | res = c + 10 - 'a'; | ||
830 | return true; | ||
831 | } | ||
832 | return err("invalid digit '%c'", c); | ||
833 | } | ||
834 | |||
835 | bool parse_signed(soc_word_t& res) | ||
836 | { | ||
837 | char op = cur(); | ||
838 | if(op == '+' || op == '-') | ||
839 | { | ||
840 | adv(); | ||
841 | if(!parse_signed(res)) | ||
842 | return false; | ||
843 | if(op == '-') | ||
844 | res *= -1; | ||
845 | return true; | ||
846 | } | ||
847 | else if(op == '(') | ||
848 | { | ||
849 | adv(); | ||
850 | if(!parse_expression(res)) | ||
851 | return false; | ||
852 | if(cur() != ')') | ||
853 | return err("expected ')', got '%c'", cur()); | ||
854 | adv(); | ||
855 | return true; | ||
856 | } | ||
857 | else if(isdigit(op)) | ||
858 | { | ||
859 | res = op - '0'; | ||
860 | adv(); | ||
861 | int basis = 10; | ||
862 | if(op == '0' && cur() == 'x') | ||
863 | { | ||
864 | basis = 16; | ||
865 | adv(); | ||
866 | } | ||
867 | soc_word_t digit = 0; | ||
868 | while(parse_digit(cur(), basis, digit)) | ||
869 | { | ||
870 | res = res * basis + digit; | ||
871 | adv(); | ||
872 | } | ||
873 | return true; | ||
874 | } | ||
875 | else if(isalpha(op) || op == '_') | ||
876 | { | ||
877 | std::string name; | ||
878 | while(isalnum(cur()) || cur() == '_') | ||
879 | { | ||
880 | name.push_back(cur()); | ||
881 | adv(); | ||
882 | } | ||
883 | return get_variable(name, res); | ||
884 | } | ||
885 | else | ||
886 | return err("express signed expression, got '%c'", op); | ||
887 | } | ||
888 | |||
889 | bool parse_term(soc_word_t& res) | ||
890 | { | ||
891 | if(!parse_signed(res)) | ||
892 | return false; | ||
893 | while(cur() == '*' || cur() == '/' || cur() == '%') | ||
894 | { | ||
895 | char op = cur(); | ||
896 | adv(); | ||
897 | soc_word_t tmp; | ||
898 | if(!parse_signed(tmp)) | ||
899 | return false; | ||
900 | if(op == '*') | ||
901 | res *= tmp; | ||
902 | else if(tmp != 0) | ||
903 | res = op == '/' ? res / tmp : res % tmp; | ||
904 | else | ||
905 | return err("division by 0"); | ||
906 | } | ||
907 | return true; | ||
908 | } | ||
909 | |||
910 | bool parse_expression(soc_word_t& res) | ||
911 | { | ||
912 | if(!parse_term(res)) | ||
913 | return false; | ||
914 | while(!end() && (cur() == '+' || cur() == '-')) | ||
915 | { | ||
916 | char op = cur(); | ||
917 | adv(); | ||
918 | soc_word_t tmp; | ||
919 | if(!parse_term(tmp)) | ||
920 | return false; | ||
921 | if(op == '+') | ||
922 | res += tmp; | ||
923 | else | ||
924 | res -= tmp; | ||
925 | } | ||
926 | return true; | ||
927 | } | ||
928 | |||
929 | bool parse(soc_word_t& res, std::string& _error) | ||
930 | { | ||
931 | bool ok = parse_expression(res); | ||
932 | if(ok && !end()) | ||
933 | err("unexpected character '%c'", cur()); | ||
934 | _error = error; | ||
935 | return ok && end(); | ||
936 | } | ||
937 | |||
938 | virtual bool get_variable(std::string name, soc_word_t& res) | ||
939 | { | ||
940 | return err("unknown variable '%s'", name.c_str()); | ||
941 | } | ||
942 | }; | ||
943 | |||
944 | struct my_evaluator : public formula_evaluator | ||
945 | { | ||
946 | const std::map< std::string, soc_word_t>& var; | ||
947 | |||
948 | my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var) | ||
949 | :formula_evaluator(formula), var(_var) {} | ||
950 | |||
951 | virtual bool get_variable(std::string name, soc_word_t& res) | ||
952 | { | ||
953 | std::map< std::string, soc_word_t>::const_iterator it = var.find(name); | ||
954 | if(it == var.end()) | ||
955 | return formula_evaluator::get_variable(name, res); | ||
956 | else | ||
957 | { | ||
958 | res = it->second; | ||
959 | return true; | ||
960 | } | ||
961 | } | ||
962 | }; | ||
963 | |||
964 | } | ||
965 | |||
966 | bool evaluate_formula(const std::string& formula, | ||
967 | const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error) | ||
968 | { | ||
969 | my_evaluator e(formula, var); | ||
970 | return e.parse(result, error); | ||
971 | } | ||
972 | |||
973 | /** WARNING we need to call xmlInitParser() to init libxml2 but it needs to | ||
974 | * called from the main thread, which is a super strong requirement, so do it | ||
975 | * using a static constructor */ | ||
976 | namespace | ||
977 | { | ||
978 | class xml_parser_init | ||
979 | { | ||
980 | public: | ||
981 | xml_parser_init() | ||
982 | { | ||
983 | xmlInitParser(); | ||
984 | } | ||
985 | }; | ||
986 | |||
987 | xml_parser_init __xml_parser_init; | ||
988 | } | ||
989 | |||
990 | } // soc_desc_v1 | ||
diff --git a/utils/regtools/qeditor/backend.cpp b/utils/regtools/qeditor/backend.cpp index 7091a0c77f..cbaff7370d 100644 --- a/utils/regtools/qeditor/backend.cpp +++ b/utils/regtools/qeditor/backend.cpp | |||
@@ -35,8 +35,8 @@ SocFile::SocFile() | |||
35 | SocFile::SocFile(const QString& filename) | 35 | SocFile::SocFile(const QString& filename) |
36 | :m_filename(filename) | 36 | :m_filename(filename) |
37 | { | 37 | { |
38 | m_valid = soc_desc_parse_xml(filename.toStdString(), m_soc); | 38 | m_valid = parse_xml(filename.toStdString(), m_soc); |
39 | soc_desc_normalize(m_soc); | 39 | normalize(m_soc); |
40 | } | 40 | } |
41 | 41 | ||
42 | bool SocFile::IsValid() | 42 | bool SocFile::IsValid() |
diff --git a/utils/regtools/qeditor/backend.h b/utils/regtools/qeditor/backend.h index b2f53c9ed5..934c1c359b 100644 --- a/utils/regtools/qeditor/backend.h +++ b/utils/regtools/qeditor/backend.h | |||
@@ -29,7 +29,11 @@ | |||
29 | #ifdef HAVE_HWSTUB | 29 | #ifdef HAVE_HWSTUB |
30 | #include "hwstub.h" | 30 | #include "hwstub.h" |
31 | #endif | 31 | #endif |
32 | #include "soc_desc.hpp" | 32 | #include "soc_desc_v1.hpp" |
33 | |||
34 | /* we don't want to import the entire soc_desc except for a few selected | ||
35 | * pieces */ | ||
36 | using namespace soc_desc_v1; | ||
33 | 37 | ||
34 | class IoBackend : public QObject | 38 | class IoBackend : public QObject |
35 | { | 39 | { |
diff --git a/utils/regtools/qeditor/mainwindow.cpp b/utils/regtools/qeditor/mainwindow.cpp index 9cea728d6e..b44306d701 100644 --- a/utils/regtools/qeditor/mainwindow.cpp +++ b/utils/regtools/qeditor/mainwindow.cpp | |||
@@ -161,12 +161,14 @@ void MainWindow::OnQuit() | |||
161 | 161 | ||
162 | void MainWindow::OnAbout() | 162 | void MainWindow::OnAbout() |
163 | { | 163 | { |
164 | QString soc_desc_ver = QString("%1.%2.%3").arg(MAJOR_VERSION) | ||
165 | .arg(MINOR_VERSION).arg(REVISION_VERSION); | ||
164 | QMessageBox::about(this, "About", | 166 | QMessageBox::about(this, "About", |
165 | "<h1>QEditor</h1>" | 167 | "<h1>QEditor</h1>" |
166 | "<h2>Version "APP_VERSION"</h2>" | 168 | "<h2>Version "APP_VERSION"</h2>" |
167 | "<p>Written by Amaury Pouly</p>" | 169 | "<p>Written by Amaury Pouly</p>" |
168 | "<p>Libraries:</p>" | 170 | "<p>Libraries:</p>" |
169 | "<ul><li>soc_desc: "SOCDESC_VERSION"</li>" | 171 | "<ul><li>soc_desc: " + soc_desc_ver + "</li>" |
170 | #ifdef HAVE_HWSTUB | 172 | #ifdef HAVE_HWSTUB |
171 | "<li>hwstub: "HWSTUB_VERSION"</li>" | 173 | "<li>hwstub: "HWSTUB_VERSION"</li>" |
172 | #else | 174 | #else |
diff --git a/utils/regtools/qeditor/qeditor.pro b/utils/regtools/qeditor/qeditor.pro index eecc0aba3f..265cc93171 100644 --- a/utils/regtools/qeditor/qeditor.pro +++ b/utils/regtools/qeditor/qeditor.pro | |||
@@ -5,14 +5,14 @@ HEADERS += mainwindow.h backend.h regtab.h analyser.h settings.h \ | |||
5 | SOURCES += main.cpp mainwindow.cpp regtab.cpp backend.cpp analyser.cpp \ | 5 | SOURCES += main.cpp mainwindow.cpp regtab.cpp backend.cpp analyser.cpp \ |
6 | std_analysers.cpp settings.cpp utils.cpp regdisplaypanel.cpp regedit.cpp | 6 | std_analysers.cpp settings.cpp utils.cpp regdisplaypanel.cpp regedit.cpp |
7 | LIBS += -L../lib/ -lsocdesc -lxml2 | 7 | LIBS += -L../lib/ -lsocdesc -lxml2 |
8 | INCLUDEPATH += ../lib/ ../../hwstub/lib | 8 | INCLUDEPATH += ../include/ ../../hwstub/lib |
9 | DEPENDPATH += ../ | 9 | DEPENDPATH += ../ |
10 | 10 | ||
11 | libsocdesc.commands = cd ../lib && make | 11 | libsocdesc.commands = cd ../lib && make |
12 | QMAKE_EXTRA_TARGETS += libsocdesc | 12 | QMAKE_EXTRA_TARGETS += libsocdesc |
13 | PRE_TARGETDEPS += libsocdesc | 13 | PRE_TARGETDEPS += libsocdesc |
14 | 14 | ||
15 | VERSION = 2.0.4 | 15 | VERSION = 2.1.0 |
16 | 16 | ||
17 | DEFINES += APP_VERSION=\\\"$$VERSION\\\" | 17 | DEFINES += APP_VERSION=\\\"$$VERSION\\\" |
18 | 18 | ||
diff --git a/utils/regtools/qeditor/regedit.cpp b/utils/regtools/qeditor/regedit.cpp index 851e054eb0..8b4bfb7c49 100644 --- a/utils/regtools/qeditor/regedit.cpp +++ b/utils/regtools/qeditor/regedit.cpp | |||
@@ -421,7 +421,7 @@ void RegEditPanel::OnFormulaGenerate(bool checked) | |||
421 | map["n"] = n; | 421 | map["n"] = n; |
422 | std::string err; | 422 | std::string err; |
423 | soc_word_t res; | 423 | soc_word_t res; |
424 | if(!soc_desc_evaluate_formula(formula, map, res, err)) | 424 | if(!evaluate_formula(formula, map, res, err)) |
425 | { | 425 | { |
426 | qDebug() << "Cannot evaluator " << QString::fromStdString(formula) | 426 | qDebug() << "Cannot evaluator " << QString::fromStdString(formula) |
427 | << "for n=" << n << ": " << QString::fromStdString(err); | 427 | << "for n=" << n << ": " << QString::fromStdString(err); |
@@ -1051,8 +1051,8 @@ void RegEdit::OnNew() | |||
1051 | 1051 | ||
1052 | bool RegEdit::SaveSocFile(const QString& filename) | 1052 | bool RegEdit::SaveSocFile(const QString& filename) |
1053 | { | 1053 | { |
1054 | soc_desc_normalize(m_cur_socfile.GetSoc()); | 1054 | normalize(m_cur_socfile.GetSoc()); |
1055 | if(!soc_desc_produce_xml(filename.toStdString(), m_cur_socfile.GetSoc())) | 1055 | if(!produce_xml(filename.toStdString(), m_cur_socfile.GetSoc())) |
1056 | { | 1056 | { |
1057 | QMessageBox::warning(this, "The description was not saved", | 1057 | QMessageBox::warning(this, "The description was not saved", |
1058 | "There was an error when saving the file"); | 1058 | "There was an error when saving the file"); |
diff --git a/utils/regtools/qeditor/utils.cpp b/utils/regtools/qeditor/utils.cpp index e4a872ed49..2ee1bc6db5 100644 --- a/utils/regtools/qeditor/utils.cpp +++ b/utils/regtools/qeditor/utils.cpp | |||
@@ -1004,7 +1004,7 @@ void MyTextEditor::SetReadOnly(bool en) | |||
1004 | if(en) | 1004 | if(en) |
1005 | m_toolbar->hide(); | 1005 | m_toolbar->hide(); |
1006 | else | 1006 | else |
1007 | m_toolbar->hide(); | 1007 | m_toolbar->show(); |
1008 | m_edit->setReadOnly(en); | 1008 | m_edit->setReadOnly(en); |
1009 | } | 1009 | } |
1010 | 1010 | ||
diff --git a/utils/regtools/swiss_knife.cpp b/utils/regtools/swiss_knife.cpp new file mode 100644 index 0000000000..eaa2519a27 --- /dev/null +++ b/utils/regtools/swiss_knife.cpp | |||
@@ -0,0 +1,612 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "soc_desc.hpp" | ||
22 | #include "soc_desc_v1.hpp" | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <map> | ||
26 | #include <set> | ||
27 | #include <cstring> | ||
28 | |||
29 | using namespace soc_desc; | ||
30 | |||
31 | void print_context(const error_context_t& ctx) | ||
32 | { | ||
33 | for(size_t j = 0; j < ctx.count(); j++) | ||
34 | { | ||
35 | error_t e = ctx.get(j); | ||
36 | switch(e.level()) | ||
37 | { | ||
38 | case error_t::INFO: printf("[INFO]"); break; | ||
39 | case error_t::WARNING: printf("[WARN]"); break; | ||
40 | case error_t::FATAL: printf("[FATAL]"); break; | ||
41 | default: printf("[UNK]"); break; | ||
42 | } | ||
43 | if(e.location().size() != 0) | ||
44 | printf(" %s:", e.location().c_str()); | ||
45 | printf(" %s\n", e.message().c_str()); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | bool convert_v1_to_v2(const soc_desc_v1::soc_reg_field_value_t& in, enum_t& out, error_context_t& ctx) | ||
50 | { | ||
51 | out.name = in.name; | ||
52 | out.desc = in.desc; | ||
53 | out.value = in.value; | ||
54 | return true; | ||
55 | } | ||
56 | |||
57 | bool convert_v1_to_v2(const soc_desc_v1::soc_reg_field_t& in, field_t& out, error_context_t& ctx) | ||
58 | { | ||
59 | out.name = in.name; | ||
60 | out.desc = in.desc; | ||
61 | out.pos = in.first_bit; | ||
62 | out.width = in.last_bit - in.first_bit + 1; | ||
63 | out.enum_.resize(in.value.size()); | ||
64 | for(size_t i = 0; i < in.value.size(); i++) | ||
65 | if(!convert_v1_to_v2(in.value[i], out.enum_[i], ctx)) | ||
66 | return false; | ||
67 | return true; | ||
68 | } | ||
69 | |||
70 | bool convert_v1_to_v2(const soc_desc_v1::soc_reg_addr_t& in, instance_t& out, error_context_t& ctx) | ||
71 | { | ||
72 | out.name = in.name; | ||
73 | out.type = instance_t::SINGLE; | ||
74 | out.addr = in.addr; | ||
75 | return true; | ||
76 | } | ||
77 | |||
78 | bool convert_v1_to_v2(const soc_desc_v1::soc_reg_formula_t& in, range_t& out, error_context_t& ctx) | ||
79 | { | ||
80 | out.type = range_t::FORMULA; | ||
81 | out.formula = in.string; | ||
82 | out.variable = "n"; | ||
83 | return true; | ||
84 | } | ||
85 | |||
86 | bool convert_v1_to_v2(const soc_desc_v1::soc_reg_t& in, node_t& out, error_context_t& ctx, | ||
87 | std::string _loc) | ||
88 | { | ||
89 | std::string loc = _loc + "." + in.name; | ||
90 | out.name = in.name; | ||
91 | out.desc = in.desc; | ||
92 | if(in.formula.type == soc_desc_v1::REG_FORMULA_NONE) | ||
93 | { | ||
94 | out.instance.resize(in.addr.size()); | ||
95 | for(size_t i = 0; i < in.addr.size(); i++) | ||
96 | if(!convert_v1_to_v2(in.addr[i], out.instance[i], ctx)) | ||
97 | return false; | ||
98 | } | ||
99 | else | ||
100 | { | ||
101 | out.instance.resize(1); | ||
102 | out.instance[0].name = in.name; | ||
103 | out.instance[0].type = instance_t::RANGE; | ||
104 | out.instance[0].range.first = 0; | ||
105 | out.instance[0].range.count = in.addr.size(); | ||
106 | /* check if formula is base/stride */ | ||
107 | bool is_stride = true; | ||
108 | soc_word_t base = 0, stride = 0; | ||
109 | if(in.addr.size() <= 1) | ||
110 | { | ||
111 | ctx.add(error_t(error_t::WARNING, loc, | ||
112 | "register uses a formula but has only one instance")); | ||
113 | is_stride = false; | ||
114 | } | ||
115 | else | ||
116 | { | ||
117 | base = in.addr[0].addr; | ||
118 | stride = in.addr[1].addr - base; | ||
119 | for(size_t i = 0; i < in.addr.size(); i++) | ||
120 | if(base + i * stride != in.addr[i].addr) | ||
121 | is_stride = false; | ||
122 | } | ||
123 | |||
124 | if(is_stride) | ||
125 | { | ||
126 | ctx.add(error_t(error_t::INFO, loc, "promoted formula to base/stride")); | ||
127 | out.instance[0].range.type = range_t::STRIDE; | ||
128 | out.instance[0].range.base = base; | ||
129 | out.instance[0].range.stride = stride; | ||
130 | } | ||
131 | else if(!convert_v1_to_v2(in.formula, out.instance[0].range, ctx)) | ||
132 | return false; | ||
133 | } | ||
134 | out.register_.resize(1); | ||
135 | out.register_[0].width = 32; | ||
136 | out.register_[0].field.resize(in.field.size()); | ||
137 | for(size_t i = 0; i < in.field.size(); i++) | ||
138 | if(!convert_v1_to_v2(in.field[i], out.register_[0].field[i], ctx)) | ||
139 | return false; | ||
140 | /* sct */ | ||
141 | if(in.flags & soc_desc_v1::REG_HAS_SCT) | ||
142 | { | ||
143 | out.node.resize(1); | ||
144 | out.node[0].name = "SCT"; | ||
145 | out.node[0].instance.resize(3); | ||
146 | const char *names[3] = {"SET", "CLR", "TOG"}; | ||
147 | for(size_t i = 0; i < 3; i++) | ||
148 | { | ||
149 | out.node[0].instance[i].name = names[i]; | ||
150 | out.node[0].instance[i].type = instance_t::SINGLE; | ||
151 | out.node[0].instance[i].addr = 4 + i *4; | ||
152 | } | ||
153 | } | ||
154 | return true; | ||
155 | } | ||
156 | |||
157 | bool convert_v1_to_v2(const soc_desc_v1::soc_dev_addr_t& in, instance_t& out, error_context_t& ctx) | ||
158 | { | ||
159 | out.name = in.name; | ||
160 | out.type = instance_t::SINGLE; | ||
161 | out.addr = in.addr; | ||
162 | return true; | ||
163 | } | ||
164 | |||
165 | bool convert_v1_to_v2(const soc_desc_v1::soc_dev_t& in, node_t& out, error_context_t& ctx, | ||
166 | std::string _loc) | ||
167 | { | ||
168 | std::string loc = _loc + "." + in.name; | ||
169 | if(!in.version.empty()) | ||
170 | ctx.add(error_t(error_t::INFO, loc, "dropped version")); | ||
171 | out.name = in.name; | ||
172 | out.title = in.long_name; | ||
173 | out.desc = in.desc; | ||
174 | out.instance.resize(in.addr.size()); | ||
175 | for(size_t i = 0; i < in.addr.size(); i++) | ||
176 | if(!convert_v1_to_v2(in.addr[i], out.instance[i], ctx)) | ||
177 | return false; | ||
178 | out.node.resize(in.reg.size()); | ||
179 | for(size_t i = 0; i < in.reg.size(); i++) | ||
180 | if(!convert_v1_to_v2(in.reg[i], out.node[i], ctx, loc)) | ||
181 | return false; | ||
182 | return true; | ||
183 | } | ||
184 | |||
185 | bool convert_v1_to_v2(const soc_desc_v1::soc_t& in, soc_t& out, error_context_t& ctx) | ||
186 | { | ||
187 | out.name = in.name; | ||
188 | out.title = in.desc; | ||
189 | out.node.resize(in.dev.size()); | ||
190 | for(size_t i = 0; i < in.dev.size(); i++) | ||
191 | if(!convert_v1_to_v2(in.dev[i], out.node[i], ctx, in.name)) | ||
192 | return false; | ||
193 | return true; | ||
194 | } | ||
195 | |||
196 | int do_convert(int argc, char **argv) | ||
197 | { | ||
198 | if(argc != 2) | ||
199 | return printf("convert mode expects two arguments\n"); | ||
200 | soc_desc_v1::soc_t soc; | ||
201 | if(!soc_desc_v1::parse_xml(argv[0], soc)) | ||
202 | return printf("cannot read file '%s'\n", argv[0]); | ||
203 | error_context_t ctx; | ||
204 | soc_t new_soc; | ||
205 | if(!convert_v1_to_v2(soc, new_soc, ctx)) | ||
206 | { | ||
207 | print_context(ctx); | ||
208 | return printf("cannot convert from v1 to v2\n"); | ||
209 | } | ||
210 | if(!produce_xml(argv[1], new_soc, ctx)) | ||
211 | { | ||
212 | print_context(ctx); | ||
213 | return printf("cannot write file '%s'\n", argv[1]); | ||
214 | } | ||
215 | print_context(ctx); | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | int do_read(int argc, char **argv) | ||
220 | { | ||
221 | for(int i = 0; i < argc; i++) | ||
222 | { | ||
223 | error_context_t ctx; | ||
224 | soc_t soc; | ||
225 | bool ret = parse_xml(argv[i], soc, ctx); | ||
226 | if(ctx.count() != 0) | ||
227 | printf("In file %s:\n", argv[i]); | ||
228 | print_context(ctx); | ||
229 | if(!ret) | ||
230 | { | ||
231 | printf("cannot parse file '%s'\n", argv[i]); | ||
232 | continue; | ||
233 | } | ||
234 | } | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | int do_eval(int argc, char **argv) | ||
239 | { | ||
240 | std::map< std::string, soc_word_t > map; | ||
241 | for(int i = 0; i < argc; i++) | ||
242 | { | ||
243 | std::string formula(argv[i]); | ||
244 | soc_word_t result; | ||
245 | if(strcmp(argv[i], "--var") == 0) | ||
246 | { | ||
247 | if(i + 1 >= argc) | ||
248 | break; | ||
249 | i++; | ||
250 | std::string str(argv[i]); | ||
251 | size_t pos = str.find('='); | ||
252 | if(pos == std::string::npos) | ||
253 | { | ||
254 | printf("invalid variable string '%s'\n", str.c_str()); | ||
255 | continue; | ||
256 | } | ||
257 | std::string name = str.substr(0, pos); | ||
258 | std::string val = str.substr(pos + 1); | ||
259 | char *end; | ||
260 | soc_word_t v = strtoul(val.c_str(), &end, 0); | ||
261 | if(*end) | ||
262 | { | ||
263 | printf("invalid variable string '%s'\n", str.c_str()); | ||
264 | continue; | ||
265 | } | ||
266 | printf("%s = %#lx\n", name.c_str(), (unsigned long)v); | ||
267 | map[name] = v; | ||
268 | continue; | ||
269 | } | ||
270 | error_context_t ctx; | ||
271 | if(!evaluate_formula(formula, map, result, "", ctx)) | ||
272 | { | ||
273 | print_context(ctx); | ||
274 | printf("cannot parse '%s'\n", formula.c_str()); | ||
275 | } | ||
276 | else | ||
277 | printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result); | ||
278 | } | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | int do_write(int argc, char **argv) | ||
283 | { | ||
284 | if(argc != 2) | ||
285 | return printf("write mode expects two arguments\n"); | ||
286 | soc_t soc; | ||
287 | error_context_t ctx; | ||
288 | if(!parse_xml(argv[0], soc, ctx)) | ||
289 | { | ||
290 | print_context(ctx); | ||
291 | return printf("cannot read file '%s'\n", argv[0]); | ||
292 | } | ||
293 | if(!produce_xml(argv[1], soc, ctx)) | ||
294 | { | ||
295 | print_context(ctx); | ||
296 | return printf("cannot write file '%s'\n", argv[1]); | ||
297 | } | ||
298 | print_context(ctx); | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | void check_name(const std::string& path, const std::string& name, error_context_t& ctx) | ||
303 | { | ||
304 | if(name.empty()) | ||
305 | ctx.add(error_t(error_t::FATAL, path, "name is empty")); | ||
306 | for(size_t i = 0; i < name.size(); i++) | ||
307 | if(!isalnum(name[i]) && name[i] != '_') | ||
308 | ctx.add(error_t(error_t::FATAL, path, "name '" + name + | ||
309 | "' must only contain alphanumeric characters or '_'")); | ||
310 | } | ||
311 | |||
312 | void check_instance(const std::string& _path, const instance_t& inst, error_context_t& ctx) | ||
313 | { | ||
314 | std::string path = _path + "." + inst.name; | ||
315 | check_name(path, inst.name, ctx); | ||
316 | if(inst.type == instance_t::RANGE) | ||
317 | { | ||
318 | if(inst.range.type == range_t::FORMULA) | ||
319 | { | ||
320 | check_name(path + ".<formula variable>", inst.range.variable, ctx); | ||
321 | /* try to parse formula */ | ||
322 | std::map< std::string, soc_word_t> var; | ||
323 | var[inst.range.variable] = inst.range.first; | ||
324 | soc_word_t res; | ||
325 | if(!evaluate_formula(inst.range.formula, var, res, path + ".<formula>", ctx)) | ||
326 | ctx.add(error_t(error_t::FATAL, path + ".<formula>", | ||
327 | "cannot evaluate formula")); | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | |||
332 | void check_field(const std::string& _path, const field_t& field, error_context_t& ctx) | ||
333 | { | ||
334 | std::string path = _path + "." + field.name; | ||
335 | check_name(path, field.name, ctx); | ||
336 | if(field.width == 0) | ||
337 | ctx.add(error_t(error_t::WARNING, path, "field has width 0")); | ||
338 | soc_word_t max = field.bitmask() >> field.pos; | ||
339 | std::set< std::string > names; | ||
340 | std::map< soc_word_t, std::string > map; | ||
341 | for(size_t i = 0; i < field.enum_.size(); i++) | ||
342 | { | ||
343 | soc_word_t v = field.enum_[i].value; | ||
344 | std::string n = field.enum_[i].name; | ||
345 | std::string path_ = path + "." + n; | ||
346 | check_name(path_, n, ctx); | ||
347 | if(v > max) | ||
348 | ctx.add(error_t(error_t::FATAL, path_, "value does not fit into the field")); | ||
349 | if(names.find(n) != names.end()) | ||
350 | ctx.add(error_t(error_t::FATAL, path, "duplicate name '" + n + "' in enums")); | ||
351 | names.insert(n); | ||
352 | if(map.find(v) != map.end()) | ||
353 | ctx.add(error_t(error_t::WARNING, path, "'" + n + "' and '" + map[v] + "' have the same value")); | ||
354 | map[v] = n; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | void check_register(const std::string& _path, const soc_desc::register_t& reg, error_context_t& ctx) | ||
359 | { | ||
360 | std::string path = _path + ".<register>"; | ||
361 | if(reg.width != 8 && reg.width != 16 && reg.width != 32) | ||
362 | ctx.add(error_t(error_t::WARNING, path, "width is not 8, 16 or 32")); | ||
363 | for(size_t i = 0; i < reg.field.size(); i++) | ||
364 | check_field(path, reg.field[i], ctx); | ||
365 | std::set< std::string > names; | ||
366 | soc_word_t bitmap = 0; | ||
367 | for(size_t i = 0; i < reg.field.size(); i++) | ||
368 | { | ||
369 | std::string n = reg.field[i].name; | ||
370 | if(names.find(n) != names.end()) | ||
371 | ctx.add(error_t(error_t::FATAL, path, "duplicate name '" + n + "' in fields")); | ||
372 | if(reg.field[i].pos + reg.field[i].width > reg.width) | ||
373 | ctx.add(error_t(error_t::FATAL, path, "field '" + n + "' does not fit into the register")); | ||
374 | names.insert(n); | ||
375 | if(bitmap & reg.field[i].bitmask()) | ||
376 | { | ||
377 | /* find the duplicate to ease debugging */ | ||
378 | for(size_t j = 0; j < i; j++) | ||
379 | if(reg.field[j].bitmask() & reg.field[i].bitmask()) | ||
380 | ctx.add(error_t(error_t::FATAL, path, "overlap between fields '" + | ||
381 | reg.field[j].name + "' and '" + n + "'")); | ||
382 | } | ||
383 | bitmap |= reg.field[i].bitmask(); | ||
384 | } | ||
385 | } | ||
386 | |||
387 | void check_nodes(const std::string& path, const std::vector< node_t >& nodes, | ||
388 | error_context_t& ctx); | ||
389 | |||
390 | void check_node(const std::string& _path, const node_t& node, error_context_t& ctx) | ||
391 | { | ||
392 | std::string path = _path + "." + node.name; | ||
393 | check_name(_path, node.name, ctx); | ||
394 | if(node.instance.empty()) | ||
395 | ctx.add(error_t(error_t::FATAL, path, "subnode with no instances")); | ||
396 | for(size_t j = 0; j < node.instance.size(); j++) | ||
397 | check_instance(path, node.instance[j], ctx); | ||
398 | for(size_t i = 0; i < node.register_.size(); i++) | ||
399 | check_register(path, node.register_[i], ctx); | ||
400 | check_nodes(path, node.node, ctx); | ||
401 | } | ||
402 | |||
403 | void check_nodes(const std::string& path, const std::vector< node_t >& nodes, | ||
404 | error_context_t& ctx) | ||
405 | { | ||
406 | for(size_t i = 0; i < nodes.size(); i++) | ||
407 | check_node(path, nodes[i], ctx); | ||
408 | /* gather all instance names */ | ||
409 | std::set< std::string > names; | ||
410 | for(size_t i = 0; i < nodes.size(); i++) | ||
411 | for(size_t j = 0; j < nodes[i].instance.size(); j++) | ||
412 | { | ||
413 | std::string n = nodes[i].instance[j].name; | ||
414 | if(names.find(n) != names.end()) | ||
415 | ctx.add(error_t(error_t::FATAL, path, "duplicate instance name '" + | ||
416 | n + "' in subnodes")); | ||
417 | names.insert(n); | ||
418 | } | ||
419 | /* gather all node names */ | ||
420 | names.clear(); | ||
421 | for(size_t i = 0; i < nodes.size(); i++) | ||
422 | { | ||
423 | std::string n = nodes[i].name; | ||
424 | if(names.find(n) != names.end()) | ||
425 | ctx.add(error_t(error_t::FATAL, path, "duplicate node name '" + n + | ||
426 | "' in subnodes")); | ||
427 | names.insert(n); | ||
428 | } | ||
429 | } | ||
430 | |||
431 | void do_check(soc_t& soc, error_context_t& ctx) | ||
432 | { | ||
433 | check_name(soc.name, soc.name, ctx); | ||
434 | check_nodes(soc.name, soc.node, ctx); | ||
435 | } | ||
436 | |||
437 | int do_check(int argc, char **argv) | ||
438 | { | ||
439 | for(int i = 0; i < argc; i++) | ||
440 | { | ||
441 | error_context_t ctx; | ||
442 | soc_t soc; | ||
443 | bool ret = parse_xml(argv[i], soc, ctx); | ||
444 | if(ret) | ||
445 | do_check(soc, ctx); | ||
446 | if(ctx.count() != 0) | ||
447 | printf("In file %s:\n", argv[i]); | ||
448 | print_context(ctx); | ||
449 | if(!ret) | ||
450 | { | ||
451 | printf("cannot parse file '%s'\n", argv[i]); | ||
452 | continue; | ||
453 | } | ||
454 | } | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | const unsigned DUMP_NODES = 1 << 0; | ||
459 | const unsigned DUMP_INSTANCES = 1 << 1; | ||
460 | const unsigned DUMP_VERBOSE = 1 << 2; | ||
461 | const unsigned DUMP_REGISTERS = 1 << 3; | ||
462 | |||
463 | void print_path(node_ref_t node, bool nl = true) | ||
464 | { | ||
465 | printf("%s", node.soc().get()->name.c_str()); | ||
466 | std::vector< std::string > path = node.path(); | ||
467 | for(size_t i = 0; i < path.size(); i++) | ||
468 | printf(".%s", path[i].c_str()); | ||
469 | if(nl) | ||
470 | printf("\n"); | ||
471 | } | ||
472 | |||
473 | void print_inst(node_inst_t inst, bool end = true) | ||
474 | { | ||
475 | if(!inst.is_root()) | ||
476 | { | ||
477 | print_inst(inst.parent(), false); | ||
478 | printf(".%s", inst.name().c_str()); | ||
479 | if(inst.is_indexed()) | ||
480 | printf("[%u]", (unsigned)inst.index()); | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | printf("%s", inst.soc().get()->name.c_str()); | ||
485 | } | ||
486 | if(end) | ||
487 | printf(" @ %#x\n", inst.addr()); | ||
488 | } | ||
489 | |||
490 | void print_reg(register_ref_t reg, unsigned flags) | ||
491 | { | ||
492 | if(!(flags & DUMP_REGISTERS)) | ||
493 | return; | ||
494 | node_ref_t node = reg.node(); | ||
495 | soc_desc::register_t *r = reg.get(); | ||
496 | print_path(node, false); | ||
497 | printf(":width=%u\n", (unsigned)r->width); | ||
498 | std::vector< field_ref_t > fields = reg.fields(); | ||
499 | for(size_t i = 0; i < fields.size(); i++) | ||
500 | { | ||
501 | field_t *f = fields[i].get(); | ||
502 | print_path(node, false); | ||
503 | if(f->width == 1) | ||
504 | printf(":[%u]=", (unsigned)f->pos); | ||
505 | else | ||
506 | printf(":[%u-%u]=", (unsigned)(f->pos + f->width - 1), (unsigned)f->pos); | ||
507 | printf("%s\n", f->name.c_str()); | ||
508 | } | ||
509 | } | ||
510 | |||
511 | void do_dump(node_ref_t node, unsigned flags) | ||
512 | { | ||
513 | print_path(node); | ||
514 | if(node.reg().node() == node) | ||
515 | print_reg(node.reg(), flags); | ||
516 | std::vector< node_ref_t > children = node.children(); | ||
517 | for(size_t i = 0; i < children.size(); i++) | ||
518 | do_dump(children[i], flags); | ||
519 | } | ||
520 | |||
521 | void do_dump(node_inst_t inst, unsigned flags) | ||
522 | { | ||
523 | print_inst(inst); | ||
524 | std::vector< node_inst_t > children = inst.children(); | ||
525 | for(size_t i = 0; i < children.size(); i++) | ||
526 | do_dump(children[i], flags); | ||
527 | } | ||
528 | |||
529 | void do_dump(soc_t& soc, unsigned flags) | ||
530 | { | ||
531 | soc_ref_t ref(&soc); | ||
532 | if(flags & DUMP_NODES) | ||
533 | do_dump(ref.root(), flags); | ||
534 | if(flags & DUMP_INSTANCES) | ||
535 | do_dump(ref.root_inst(), flags); | ||
536 | } | ||
537 | |||
538 | int do_dump(int argc, char **argv) | ||
539 | { | ||
540 | unsigned flags = 0; | ||
541 | int i = 0; | ||
542 | for(; i < argc; i++) | ||
543 | { | ||
544 | if(strcmp(argv[i], "--nodes") == 0) | ||
545 | flags |= DUMP_NODES; | ||
546 | else if(strcmp(argv[i], "--instances") == 0) | ||
547 | flags |= DUMP_INSTANCES; | ||
548 | else if(strcmp(argv[i], "--verbose") == 0) | ||
549 | flags |= DUMP_VERBOSE; | ||
550 | else if(strcmp(argv[i], "--registers") == 0) | ||
551 | flags |= DUMP_REGISTERS; | ||
552 | else | ||
553 | break; | ||
554 | } | ||
555 | if(i == argc) | ||
556 | { | ||
557 | printf("you must specify at least one file\n"); | ||
558 | return 1; | ||
559 | } | ||
560 | for(; i < argc; i++) | ||
561 | { | ||
562 | error_context_t ctx; | ||
563 | soc_t soc; | ||
564 | bool ret = parse_xml(argv[i], soc, ctx); | ||
565 | if(ret) | ||
566 | do_dump(soc, flags); | ||
567 | if(ctx.count() != 0) | ||
568 | printf("In file %s:\n", argv[i]); | ||
569 | print_context(ctx); | ||
570 | if(!ret) | ||
571 | { | ||
572 | printf("cannot parse file '%s'\n", argv[i]); | ||
573 | continue; | ||
574 | } | ||
575 | } | ||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | void usage() | ||
580 | { | ||
581 | printf("usage: swiss_knife <mode> [options]\n"); | ||
582 | printf("modes:\n"); | ||
583 | printf(" read <files...>\n"); | ||
584 | printf(" write <read file> <write file>\n"); | ||
585 | printf(" eval [<formula>|--var <name>=<val>]...\n"); | ||
586 | printf(" convert <input file> <output file>\n"); | ||
587 | printf(" check <files...>\n"); | ||
588 | printf(" dump [--nodes] [--instances] [--registers] [--verbose] <files...>\n"); | ||
589 | exit(1); | ||
590 | } | ||
591 | |||
592 | int main(int argc, char **argv) | ||
593 | { | ||
594 | if(argc < 2) | ||
595 | usage(); | ||
596 | std::string mode = argv[1]; | ||
597 | if(mode == "read") | ||
598 | return do_read(argc - 2, argv + 2); | ||
599 | else if(mode == "write") | ||
600 | return do_write(argc - 2, argv + 2); | ||
601 | else if(mode == "eval") | ||
602 | return do_eval(argc - 2, argv + 2); | ||
603 | else if(mode == "convert") | ||
604 | return do_convert(argc - 2, argv + 2); | ||
605 | else if(mode == "check") | ||
606 | return do_check(argc - 2, argv + 2); | ||
607 | else if(mode == "dump") | ||
608 | return do_dump(argc - 2, argv + 2); | ||
609 | else | ||
610 | usage(); | ||
611 | return 0; | ||
612 | } \ No newline at end of file | ||
diff --git a/utils/regtools/tester.cpp b/utils/regtools/tester_v1.cpp index 1beba5fe9b..3ece431531 100644 --- a/utils/regtools/tester.cpp +++ b/utils/regtools/tester_v1.cpp | |||
@@ -18,12 +18,14 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "soc_desc.hpp" | 21 | #include "soc_desc_v1.hpp" |
22 | #include <stdio.h> | 22 | #include <stdio.h> |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <map> | 24 | #include <map> |
25 | #include <cstring> | 25 | #include <cstring> |
26 | 26 | ||
27 | using namespace soc_desc_v1; | ||
28 | |||
27 | template< typename T > | 29 | template< typename T > |
28 | bool build_map(const char *type, const std::vector< T >& vec, | 30 | bool build_map(const char *type, const std::vector< T >& vec, |
29 | std::map< std::string, size_t >& map) | 31 | std::map< std::string, size_t >& map) |
@@ -244,9 +246,9 @@ int do_compare(int argc, char **argv) | |||
244 | if(argc != 2) | 246 | if(argc != 2) |
245 | return printf("compare mode expects two arguments\n"); | 247 | return printf("compare mode expects two arguments\n"); |
246 | soc_t soc[2]; | 248 | soc_t soc[2]; |
247 | if(!soc_desc_parse_xml(argv[0], soc[0])) | 249 | if(!parse_xml(argv[0], soc[0])) |
248 | return printf("cannot read file '%s'\n", argv[0]); | 250 | return printf("cannot read file '%s'\n", argv[0]); |
249 | if(!soc_desc_parse_xml(argv[1], soc[1])) | 251 | if(!parse_xml(argv[1], soc[1])) |
250 | return printf("cannot read file '%s'\n", argv[1]); | 252 | return printf("cannot read file '%s'\n", argv[1]); |
251 | if(compare_soc(soc[0], soc[1])) | 253 | if(compare_soc(soc[0], soc[1])) |
252 | printf("Files are identical.\n"); | 254 | printf("Files are identical.\n"); |
@@ -258,9 +260,9 @@ int do_write(int argc, char **argv) | |||
258 | if(argc != 2) | 260 | if(argc != 2) |
259 | return printf("write mode expects two arguments\n"); | 261 | return printf("write mode expects two arguments\n"); |
260 | soc_t soc; | 262 | soc_t soc; |
261 | if(!soc_desc_parse_xml(argv[0], soc)) | 263 | if(!parse_xml(argv[0], soc)) |
262 | return printf("cannot read file '%s'\n", argv[0]); | 264 | return printf("cannot read file '%s'\n", argv[0]); |
263 | if(!soc_desc_produce_xml(argv[1], soc)) | 265 | if(!produce_xml(argv[1], soc)) |
264 | return printf("cannot write file '%s'\n", argv[1]); | 266 | return printf("cannot write file '%s'\n", argv[1]); |
265 | return 0; | 267 | return 0; |
266 | } | 268 | } |
@@ -270,7 +272,7 @@ int do_check(int argc, char **argv) | |||
270 | for(int i = 0; i < argc; i++) | 272 | for(int i = 0; i < argc; i++) |
271 | { | 273 | { |
272 | soc_t soc; | 274 | soc_t soc; |
273 | if(!soc_desc_parse_xml(argv[i], soc)) | 275 | if(!parse_xml(argv[i], soc)) |
274 | { | 276 | { |
275 | printf("cannot read file '%s'\n", argv[i]); | 277 | printf("cannot read file '%s'\n", argv[i]); |
276 | continue; | 278 | continue; |
@@ -325,7 +327,7 @@ int do_eval(int argc, char **argv) | |||
325 | map[name] = v; | 327 | map[name] = v; |
326 | continue; | 328 | continue; |
327 | } | 329 | } |
328 | if(!soc_desc_evaluate_formula(formula, map, result, error)) | 330 | if(!evaluate_formula(formula, map, result, error)) |
329 | printf("error: %s\n", error.c_str()); | 331 | printf("error: %s\n", error.c_str()); |
330 | else | 332 | else |
331 | printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result); | 333 | printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result); |