| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/fit/defer.h> |
| |
| #include <cctype> |
| #include <cerrno> |
| #include <cinttypes> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstring> |
| #include <string> |
| |
| #include <hwreg/asm.h> |
| |
| namespace hwreg { |
| |
| int AsmHeader::Main(int argc, char** argv) { |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s OUTPUT_FILE INCLUDE_NAME\n", argv[0]); |
| return 1; |
| } |
| const char* filename = argv[1]; |
| const char* include_name = argv[2]; |
| int error = Output(filename, include_name); |
| if (error) { |
| fprintf(stderr, "%s: %s\n", filename, strerror(error)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| std::string AsmHeader::Output(std::string_view include_name) { |
| std::string guard("_"); |
| guard += include_name; |
| for (auto& c : guard) { |
| c = std::isalnum(c) ? static_cast<char>(std::toupper(c)) : '_'; |
| } |
| guard += '_'; |
| |
| std::string contents("// This file is generated. DO NOT EDIT!\n\n"); |
| contents += "#ifndef " + guard + "\n"; |
| contents += "#define " + guard + " 1\n"; |
| contents += "\n"; |
| contents += body_; |
| contents += "\n"; |
| contents += "#endif // " + guard + "\n"; |
| |
| return contents; |
| } |
| |
| int AsmHeader::Output(const char* filename, std::string_view include_name) { |
| std::string contents = Output(include_name); |
| |
| // Return early if the file exists and matches the contents. |
| if (FILE* f = fopen(filename, "r")) { |
| auto cleanup = fit::defer([f]() { fclose(f); }); |
| if (fseek(f, 0, SEEK_END) == 0) { |
| long int pos = ftell(f); |
| if (pos > 0) { |
| std::string old; |
| old.resize(pos); |
| rewind(f); |
| fread(old.data(), 1, pos, f); |
| if (!ferror(f) && old == contents) { |
| return 0; |
| } |
| } |
| } |
| } |
| |
| // Write the file out. |
| if (FILE* outf = fopen(filename, "w")) { |
| fwrite(contents.data(), 1, contents.size(), outf); |
| if (!ferror(outf) && fclose(outf) == 0) { |
| return 0; |
| } |
| } |
| return errno; |
| } |
| |
| AsmHeader& AsmHeader::Macro(std::string_view name, std::string_view value) { |
| body_ += "#define "; |
| body_ += name; |
| body_ += " "; |
| body_ += value; |
| body_ += "\n"; |
| return *this; |
| } |
| |
| AsmHeader& AsmHeader::Macro(std::string_view name, uint64_t value) { |
| char buf[2 + std::numeric_limits<uint64_t>::digits10 + 1]; |
| snprintf(buf, sizeof(buf), "%#" PRIx64, value); |
| return Macro(name, buf); |
| } |
| |
| void AsmHeader::FieldMacro(std::string_view prefix, const char* field_name, uint32_t bit_high_incl, |
| uint32_t bit_low) { |
| std::string name(prefix); |
| name += field_name; |
| for (auto& c : name) { |
| c = static_cast<char>(std::toupper(c)); |
| } |
| Macro(name, internal::ComputeMask<uint64_t>(bit_high_incl - bit_low + 1) << bit_low); |
| if (bit_high_incl == bit_low) { |
| // Single bits also get a bit-number macro (rendered in decimal). |
| Macro(name + "_BIT", std::to_string(bit_low)); |
| } |
| } |
| |
| void AsmHeader::RegisterMacros(std::string_view prefix, uint64_t rsvdz, uint64_t unknown) { |
| std::string name(prefix); |
| for (auto& c : name) { |
| c = static_cast<char>(std::toupper(c)); |
| } |
| if (rsvdz != 0) { |
| Macro(name + "RSVDZ", rsvdz); |
| } |
| if (unknown != 0) { |
| Macro(name + "UNKNOWN", unknown); |
| } |
| } |
| |
| } // namespace hwreg |